回答編集履歴
3
「参照渡し」の表現を厳密化
answer
CHANGED
@@ -7,9 +7,9 @@
|
|
7
7
|
ですので、「**コードの中のself.xは全て新規生成されたオブジェクト**」になっています。
|
8
8
|
よって、以下のコードで、self.xとrefxとrefx2が別IDになります。細かく言うと、`print(type(self.x), id(self.x))`の中の2箇所のself.xも、別オブジェクトになっているはずです。
|
9
9
|
|
10
|
-
ところが、以下のコードで**`refx3 = refx`ではオブジェクトの新規生成は行われません。よって
|
10
|
+
ところが、以下のコードで**`refx3 = refx`ではオブジェクトの新規生成は行われません。よってそれぞれの変数に同一のオブジェクトが代入され、IDは同一になります**。
|
11
11
|
|
12
|
-
わかりにくいのが、以下のコードの2番目の`print(type(self.x), id(self.x))`です。別オブジェクトのはずなのに、前と同じIDを示しています。これは、**同一の識別子では、Python環境によっては、オブジェクトのIDを使い回す可能性がある**ためです。性能改善やメモリ効率化のためだと思われます。この動作はPython環境依存です。質疑において、別の方とIDの出方(前と同じか異なるか)が食い違ったため、環境依存であるとわかりました。なお、**識別子が同じ場合に、インスタンスメソッドオブジェクト
|
12
|
+
わかりにくいのが、以下のコードの2番目の`print(type(self.x), id(self.x))`です。別オブジェクトのはずなのに、前と同じIDを示しています。これは、**同一の識別子の変数への代入では、Python環境によっては、オブジェクトのIDを使い回す可能性がある**ためです。性能改善やメモリ効率化のためだと思われます。この動作はPython環境依存です。質疑において、別の方とIDの出方(前と同じか異なるか)が食い違ったため、環境依存であるとわかりました。なお、**識別子が同じでIDを使い回す場合に、インスタンスメソッドオブジェクトが毎回同じIDで新規作成されるのか、それとも以前のオブジェクトを新しい変数(識別子は同じ)に代入して使い回すのか、はたまた変数そのものも同一なのか、は厳密には謎**です。最初の方が汎用的ですが、実装依存で高速性を追求して、**2番目以降の可能性もあります**。
|
13
13
|
|
14
14
|
```Python
|
15
15
|
class A():
|
2
間違った回答の修正
answer
CHANGED
@@ -1,44 +1,38 @@
|
|
1
1
|
回答が間違っていましたので、解決済ですが修正します。
|
2
|
+
・・・という回答も間違っていたので再度修正します。
|
3
|
+
@otn様の回答で合っているのですが、私がその回答の理解に手間取りました。参考になるかもしれませんので記載します。
|
2
4
|
|
3
|
-
|
5
|
+
まず@otn様の回答にありますように、「**関数オブジェクトからインスタンスメソッドオブジェクトが毎回生成される**」ことになります。具体的に今回の場合には`class 'function'>であるA.x`と`class '__main__.A'>であるself`から`<class 'method'>であるself.x`が新規生成されます。以下のコードの実行でそれがわかります。
|
4
6
|
|
7
|
+
ですので、「**コードの中のself.xは全て新規生成されたオブジェクト**」になっています。
|
5
|
-
|
8
|
+
よって、以下のコードで、self.xとrefxとrefx2が別IDになります。細かく言うと、`print(type(self.x), id(self.x))`の中の2箇所のself.xも、別オブジェクトになっているはずです。
|
6
9
|
|
7
|
-
|
10
|
+
ところが、以下のコードで**`refx3 = refx`ではオブジェクトの新規生成は行われません。よって単なる参照渡しがされるためIDは同一になります**。
|
8
11
|
|
9
|
-
|
12
|
+
わかりにくいのが、以下のコードの2番目の`print(type(self.x), id(self.x))`です。別オブジェクトのはずなのに、前と同じIDを示しています。これは、**同一の識別子では、Python環境によっては、オブジェクトのIDを使い回す可能性がある**ためです。性能改善やメモリ効率化のためだと思われます。この動作はPython環境依存です。質疑において、別の方とIDの出方(前と同じか異なるか)が食い違ったため、環境依存であるとわかりました。なお、**識別子が同じ場合に、インスタンスメソッドオブジェクトは毎回新規作成されるのか、それとも以前のオブジェクトを参照渡しして使い回すのか、は厳密には謎**です。前者の方が汎用的ですが、後者の方が高速なのと、実装依存であることから、**後者の可能性もあります**。
|
10
13
|
|
11
|
-
なお、ベストアンサーにあるドキュメントの記述「関数オブジェクトからインスタンスメソッドオブジェクトへの変換」が、このことを表しているかどうか、**確証が得られませんでした**。実行例からはself.xそのものは既に`<class 'method'>`になっておりfunctionではありません。`self.x.__func__`がfunctionです。よって、`refx = self.x`については、「関数オブジェクトからインスタンスメソッドオブジェクトへの変換」**ではありません**。
|
12
|
-
|
13
|
-
推測としては、refxという形式に変えることで、変数としてのselfを表す手段が無くなったため、selfの値を確定させる必要があり、新規のオブジェクトを生成しているものかと思います。
|
14
|
-
|
15
14
|
```Python
|
16
|
-
class A:
|
15
|
+
class A():
|
17
16
|
def __init__(self):
|
17
|
+
refx=self.x
|
18
|
-
|
18
|
+
refx2=self.x
|
19
|
+
print(type(A.x))
|
20
|
+
#<class 'function'>
|
21
|
+
print(type(self))
|
22
|
+
#<class '__main__.A'>
|
19
|
-
print(
|
23
|
+
print(type(self.x), id(self.x))
|
20
|
-
#
|
24
|
+
#<class 'method'> 140270956725312
|
21
|
-
print(
|
25
|
+
print(type(refx), id(refx))
|
22
|
-
# id(refx) = 140318265058176
|
23
|
-
print('type(self.x) =', type(self.x))
|
24
|
-
#
|
26
|
+
#<class 'method'> 140270936531840
|
25
|
-
print(
|
27
|
+
print(type(refx2), id(refx2))
|
26
|
-
#
|
28
|
+
#<class 'method'> 140270936573056
|
27
|
-
print('id(self.x.__func__) =', id(self.x.__func__))
|
28
|
-
# id(self.x.__func__) = 140318285919856
|
29
|
-
print('id(refx.__func__) =', id(refx.__func__))
|
30
|
-
# id(refx.__func__) = 140318285919856
|
31
|
-
print('id(self.x.__self__) =', id(self.x.__self__))
|
32
|
-
# id(self.x.__self__) = 140318285934896
|
33
|
-
print('id(refx.__self__) =', id(refx.__self__))
|
34
|
-
# id(refx.__self__) = 140318285934896
|
35
|
-
|
29
|
+
refx3 = refx
|
36
|
-
print(
|
30
|
+
print(type(refx3), id(refx3))
|
31
|
+
#<class 'method'> 140270936531840
|
37
|
-
|
32
|
+
print(type(self.x), id(self.x))
|
33
|
+
#<class 'method'> 140270956725312
|
38
|
-
def x(self
|
34
|
+
def x(self):
|
39
|
-
|
35
|
+
print("x")
|
40
36
|
|
41
|
-
|
37
|
+
refa=A()
|
42
|
-
print('id(a) =', id(a))
|
43
|
-
# id(a) = 140318285934896
|
44
38
|
```
|
1
間違った回答の修正
answer
CHANGED
@@ -1,15 +1,44 @@
|
|
1
|
-
|
1
|
+
回答が間違っていましたので、解決済ですが修正します。
|
2
2
|
|
3
|
-
|
3
|
+
クラス内のメソッドは、`<class 'method'>`という型になっています。いわゆる「クラスメソッド」とは異なる意味です。`<class 'method'>`は、selfとfunctionを内部に記録している構造になっています。またfunctionを呼び出す際には第一引数としてselfを与えます。**単なるfunctionとは異なります**。
|
4
4
|
|
5
|
+
この時、Pythonの動作として、質問者様の例にあるような、`refx = self.x`という形の**コピーする際に、オブジェクトの参照渡しではなく、新規にオブジェクトを生成して中身をコピーする動作をしているようです**。ただし、下記コードにあるように、`refx2 = refx`ではしっかりとコピーされており、**オブジェクト固有の動作だけでは理由がわかりません**。
|
5
6
|
|
7
|
+
なお、下記コードでは、別オブジェクトになっているself.xとrefxにおいても、中身のselfとfuncは同じオブジェクトを指しており、selfはクラス定義外で作成したオブジェクトaを指している構造がわかります。
|
8
|
+
|
9
|
+
いくつかこの動作をレポートしているやりとりはあるのですが、**公式な理由は見当たりませんでした**。
|
10
|
+
|
11
|
+
なお、ベストアンサーにあるドキュメントの記述「関数オブジェクトからインスタンスメソッドオブジェクトへの変換」が、このことを表しているかどうか、**確証が得られませんでした**。実行例からはself.xそのものは既に`<class 'method'>`になっておりfunctionではありません。`self.x.__func__`がfunctionです。よって、`refx = self.x`については、「関数オブジェクトからインスタンスメソッドオブジェクトへの変換」**ではありません**。
|
12
|
+
|
13
|
+
推測としては、refxという形式に変えることで、変数としてのselfを表す手段が無くなったため、selfの値を確定させる必要があり、新規のオブジェクトを生成しているものかと思います。
|
14
|
+
|
6
15
|
```Python
|
7
|
-
class A
|
16
|
+
class A:
|
8
17
|
def __init__(self):
|
9
|
-
refx=self.x
|
18
|
+
refx = self.x
|
10
|
-
print(id(self.x())
|
19
|
+
print('id(self.x) =', id(self.x))
|
20
|
+
# id(self.x) = 140318265099392
|
21
|
+
print('id(refx) =', id(refx))
|
22
|
+
# id(refx) = 140318265058176
|
23
|
+
print('type(self.x) =', type(self.x))
|
24
|
+
# type(self.x) = <class 'method'>
|
25
|
+
print('type(refx) =', type(refx))
|
26
|
+
# type(refx) = <class 'method'>
|
27
|
+
print('id(self.x.__func__) =', id(self.x.__func__))
|
28
|
+
# id(self.x.__func__) = 140318285919856
|
29
|
+
print('id(refx.__func__) =', id(refx.__func__))
|
30
|
+
# id(refx.__func__) = 140318285919856
|
31
|
+
print('id(self.x.__self__) =', id(self.x.__self__))
|
32
|
+
# id(self.x.__self__) = 140318285934896
|
33
|
+
print('id(refx.__self__) =', id(refx.__self__))
|
34
|
+
# id(refx.__self__) = 140318285934896
|
11
|
-
|
35
|
+
refx2 = refx
|
36
|
+
print('id(refx2) =', id(refx2))
|
37
|
+
# id(refx2) = 140318265058176
|
12
|
-
def x(self):
|
38
|
+
def x(self, num):
|
13
|
-
|
39
|
+
return num + 1
|
40
|
+
|
14
41
|
a=A()
|
42
|
+
print('id(a) =', id(a))
|
43
|
+
# id(a) = 140318285934896
|
15
44
|
```
|