回答編集履歴

3

「参照渡し」の表現を厳密化

2020/10/17 09:45

投稿

toast-uz
toast-uz

スコア3266

test CHANGED
@@ -16,11 +16,11 @@
16
16
 
17
17
 
18
18
 
19
- ところが、以下のコードで**`refx3 = refx`ではオブジェクトの新規生成は行われません。よって単なる参照渡しがされるためIDは同一になります**。
19
+ ところが、以下のコードで**`refx3 = refx`ではオブジェクトの新規生成は行われません。よってそれぞれの変数に同一のオブジェクト代入されIDは同一になります**。
20
20
 
21
21
 
22
22
 
23
- わかりにくいのが、以下のコードの2番目の`print(type(self.x), id(self.x))`です。別オブジェクトのはずなのに、前と同じIDを示しています。これは、**同一の識別子では、Python環境によっては、オブジェクトのIDを使い回す可能性がある**ためです。性能改善やメモリ効率化のためだと思われます。この動作はPython環境依存です。質疑において、別の方とIDの出方(前と同じか異なるか)が食い違ったため、環境依存であるとわかりました。なお、**識別子が同じ場合に、インスタンスメソッドオブジェクト毎回新規作成されるのか、それとも以前のオブジェクトを参照渡しして使い回すのか、は厳密には謎**です。前者の方が汎用的ですが、後者の方が高速なのと、実装依存であることから、**後者の可能性もあります**。
23
+ わかりにくいのが、以下のコードの2番目の`print(type(self.x), id(self.x))`です。別オブジェクトのはずなのに、前と同じIDを示しています。これは、**同一の識別子の変数への代入では、Python環境によっては、オブジェクトのIDを使い回す可能性がある**ためです。性能改善やメモリ効率化のためだと思われます。この動作はPython環境依存です。質疑において、別の方とIDの出方(前と同じか異なるか)が食い違ったため、環境依存であるとわかりました。なお、**識別子が同じでIDを使い回す場合に、インスタンスメソッドオブジェクト毎回同じIDで新規作成されるのか、それとも以前のオブジェクトをい変数(識別子は同じ)に代入して使い回すのか、はたまた変数そのものも同一なのか、は厳密には謎**です。最初の方が汎用的ですが、実装依存で高速性を追求して、**2番目以降の可能性もあります**。
24
24
 
25
25
 
26
26
 

2

間違った回答の修正

2020/10/17 09:45

投稿

toast-uz
toast-uz

スコア3266

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

1

間違った回答の修正

2020/10/17 06:27

投稿

toast-uz
toast-uz

スコア3266

test CHANGED
@@ -1,29 +1,87 @@
1
- 質問者様コードはx()というメソッドはなく、xというオブジェクト型のメンバ変数を参照ています。
1
+ 回答が間違っていましたので、解決済すが修正します。
2
2
 
3
3
 
4
4
 
5
- 以下うに修正すると、メソッドを参照きるようになり関数同じ扱いになっています。
5
+ クラス内メソッドは、`<class 'method'>`といなっていま。いわゆ「クラスメソッド」とは異なる意味す。`<class 'method'>`はselffunctionを内部に記録してる構造になっています。またfunctionを呼び出す際には第一引数としてselfを与えます。**単なるfunctionとは異なります**。
6
6
 
7
7
 
8
+
9
+ この時、Pythonの動作として、質問者様の例にあるような、`refx = self.x`という形の**コピーする際に、オブジェクトの参照渡しではなく、新規にオブジェクトを生成して中身をコピーする動作をしているようです**。ただし、下記コードにあるように、`refx2 = refx`ではしっかりとコピーされており、**オブジェクト固有の動作だけでは理由がわかりません**。
10
+
11
+
12
+
13
+ なお、下記コードでは、別オブジェクトになっているself.xとrefxにおいても、中身のselfとfuncは同じオブジェクトを指しており、selfはクラス定義外で作成したオブジェクトaを指している構造がわかります。
14
+
15
+
16
+
17
+ いくつかこの動作をレポートしているやりとりはあるのですが、**公式な理由は見当たりませんでした**。
18
+
19
+
20
+
21
+ なお、ベストアンサーにあるドキュメントの記述「関数オブジェクトからインスタンスメソッドオブジェクトへの変換」が、このことを表しているかどうか、**確証が得られませんでした**。実行例からはself.xそのものは既に`<class 'method'>`になっておりfunctionではありません。`self.x.__func__`がfunctionです。よって、`refx = self.x`については、「関数オブジェクトからインスタンスメソッドオブジェクトへの変換」**ではありません**。
22
+
23
+
24
+
25
+ 推測としては、refxという形式に変えることで、変数としてのselfを表す手段が無くなったため、selfの値を確定させる必要があり、新規のオブジェクトを生成しているものかと思います。
8
26
 
9
27
 
10
28
 
11
29
  ```Python
12
30
 
13
- class A():
31
+ class A:
14
32
 
15
33
  def __init__(self):
16
34
 
17
- refx=self.x()
35
+ refx = self.x
18
36
 
19
- print(id(self.x()))
37
+ print('id(self.x) =', id(self.x))
20
38
 
21
- print(id(refx))
39
+ # id(self.x) = 140318265099392
22
40
 
23
- def x(self):
41
+ print('id(refx) =', id(refx))
24
42
 
43
+ # id(refx) = 140318265058176
44
+
45
+ print('type(self.x) =', type(self.x))
46
+
47
+ # type(self.x) = <class 'method'>
48
+
49
+ print('type(refx) =', type(refx))
50
+
51
+ # type(refx) = <class 'method'>
52
+
53
+ print('id(self.x.__func__) =', id(self.x.__func__))
54
+
55
+ # id(self.x.__func__) = 140318285919856
56
+
57
+ print('id(refx.__func__) =', id(refx.__func__))
58
+
59
+ # id(refx.__func__) = 140318285919856
60
+
61
+ print('id(self.x.__self__) =', id(self.x.__self__))
62
+
63
+ # id(self.x.__self__) = 140318285934896
64
+
65
+ print('id(refx.__self__) =', id(refx.__self__))
66
+
67
+ # id(refx.__self__) = 140318285934896
68
+
69
+ refx2 = refx
70
+
25
- print("x")
71
+ print('id(refx2) =', id(refx2))
72
+
73
+ # id(refx2) = 140318265058176
74
+
75
+ def x(self, num):
76
+
77
+ return num + 1
78
+
79
+
26
80
 
27
81
  a=A()
28
82
 
83
+ print('id(a) =', id(a))
84
+
85
+ # id(a) = 140318285934896
86
+
29
87
  ```