pythonの代入はすべて参照渡しらしいが、参照先での値変更について挙動がいろいろと異なるらしい。 pythonのオブジェクトはimmutable(変更不可)なものと、mutable(変更可能)なものがあり、mutableなものについては、参照先で値が変更されると元のオブジェクトの値も変更されるようだ。
結論から言うと以下の挙動はlistがmutableで、intがimmutableなことからおこるらしい。
a = [0]*3
print(id(a[0]),id(a[1]),id(a[2]))
139808821021184 139808821021184 139808821021184
a[0] = 1
print(a)
print(id(a[0]),id(a[1]),id(a[2]))
[1, 0, 0]
139808821021216 139808821021184 139808821021184
たしかに変化した部分だけアドレスが変わっている。
b =[[0]*3] *3
print(id(b[0][0]),id(b[0][1]),id(b[1][0]))
print(id(b[0]),id(b[1]))
b[0][0] = 1
print(b)
print(id(b[0][0]),id(b[0][1]),id(b[1][0]))
139808821021184 139808821021184 139808821021184
139808366470920 139808366470920
[[1, 0, 0], [1, 0, 0], [1, 0, 0]]
139808821021216 139808821021184 139808821021216
要するになぜこれが起きるかというと、リストはmutableなオブジェクトで、b[0]~b[2]のアドレスが一致しているため、3つとも値が変更されるということらしい。
下に回避方法を書いた。
d = [[0]*3 for _ in range(3)]
print(id(d[0]),id(d[1]))
d[0][0] = 1
print(d)
139808366538632 139808366474440
[[1, 0, 0], [0, 0, 0], [0, 0, 0]]
なおintとかも普通に代入されてるやん!という突っ込みをしたくなるが、どうも以下のように代入をする場合はは新しいオブジェクトになっている模様。 変更不可なことの例としては複素数が分かりやすかった。
a =4
print(id(a))
a = 5
print(id(a))
139808821021312
139808821021344
a = 4 + 3j
a.imag = 4
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-54-6e8c72133bc5> in <module>()
1 a = 4 + 3j
----> 2 a.imag = 4
AttributeError: readonly attribute
import numpy as np
A = np.repeat([0], 3)
B = np.array([0]*3)
print(id(A[0]),id(A[1]),id(A[2]))
print(id(B[0]),id(B[1]),id(B[2]))
AA = np.tile([0],[3,3])
print(id(AA[0][0]),id(AA[1][0]),id(AA[2][0]))
AA[0][0] = 3
print(id(AA[0][0]),id(AA[1][0]),id(AA[2][0]))
c = np.array([[0]*3] *3 )
c[0][0] = 1
c
139808367363848 139808367363848 139808367363848
139808367363848 139808367363848 139808367363848
139808367363848 139808367363848 139808367363848
139808367363848 139808367363848 139808367363848
array([[1, 0, 0],
[0, 0, 0],
[0, 0, 0]])
そもそも一つのオブジェクトとして宣言されるっぽいので調査の意味がなかった。pythonマスターへの道は果てしない。
参考 http://amacbee.hatenablog.com/entry/2016/12/07/004510 twitterを見て書き始めたのだが、結局この記事の劣化版になってしまった。pythonの実装はちゃんと読もうね。