自分用pythonメモ

多次元リスト初期化の話

 例えば二次元リストを初期化したいとき。

A=dim*[dim*[0]]

 としたくなるが、これをするとAの各成分A[0],A[1],…,A[dim-1]がすべてdim*[0]を参照するようになる。言い換えると

id(A[0])==id(A[1])
#True

 がTrueになる。例えばdim=3として、上の初期化をした後に

A[0][0]=1

 とすると。A=[[1,0,0],[1,0,0],[1,0,0]]となる。つまりA[0],A[1],A[2]がすべて更新されてしまう。
 これを阻止するためには

A=[dim*[0] for _ in range(dim)]

 のように初期化する。A[0],A[1],…,A[dim-1]が別のidを持つ。

リストのdeepcopy

 ちゃんとcopy.deepcopy()を使おうという話です。リストAをBに代入したいとします。これをシンプルに 

A=[1,2,3]
B=A

と記述すると、シャローコピーになっています。つまり

A=[1,2,3]
B=A
id(B)==id(A)
#True

 なので、Bを更新するとAまで更新されてしまいます。これを対処するために、私は(誰に教わったわけでもなく)以下のようにしていました。

A=[1,2,3]
B=list(A)
id(B)==id(A)
#False
id(B[0])==id(A[0])
#True
A[0]=100
print(A)
#[100,2,3]
print(B)
#[1,2,3]

 リストAをlist函数を通して渡すことによって、Aをリスト化したものを渡すことになるので、idが異なるようになります。しかしながらリストの各成分は同じものを参照しています。リストが1次元の場合にはこれでもよいですが、多次元の場合はよろしくないです。

A=[[1,2,3],[4,5,6]]
B=list(A)
id(B)==id(A)
#False
id(B[0])==id(A[0])
#True
A[0][0]=100
print(A)
#[[100, 2, 3], [4, 5, 6]]
print(B)
#[[100, 2, 3], [4, 5, 6]]

リストA[0]の適当な成分を更新しようとすると、A[0]とB[0]が同じリストを参照しているので、B[0]まで更新されてしまいます。これを阻止したい場合には

from copy import deepcopy
A=[[1,2,3],[4,5,6]]
B=deepcopy(A)
id(B)==id(A)
#False
id(B[0])==id(A[0])
#False
A[0][0]=100
print(A)
#[[100, 2, 3], [4, 5, 6]]
print(B)
#[[1, 2, 3], [4, 5, 6]]

としなければいけません。ちゃんとdeepcopyを使おうねという自分への戒めでした。

タイトルとURLをコピーしました