Python多次元配列初期化の2つの方法と浅いコピー問題

2686 ワード

Pythonはリスト、メタグループ、辞書などのデータ構造を提供し、リストは多層にネストされ、「多次元配列」を形成することができる.最近Pythonでリストネストを用いて配列を定義する際に浅いコピーの問題に遭遇し,多次元配列の初期化の2つの方法をまとめた.
1.[0]*nコピーリスト
リスト要素を乗算する方法[[0]*a]*b]*nを使用して、要素値が0のn次元a*b配列を初期化します.a=3,b=4,n=2とすると,その初期化原理は,リスト[0]を3回コピーして[0,0]とし,[0,0]リストオブジェクトの参照を4回コピーし,初期化後の2次元配列を得ることである.[0,0,0]*4というコピー形式は浅いコピーであることに注目すべきである.listの要素については、浅いコピーでは元の要素の参照(メモリアドレス)のみが使用され、すなわちA[0][1]、A[1][1]、A[2][1]、A[3][1]は同じメモリアドレスに対応し、Aの要素A[0][1]を修正すると、対象AのA[i][1]の値が変化する.
#      list
A=[0]*4
A
[0, 0, 0, 0]
#           
A[0]=1
A
[1, 0, 0, 0] #            

#      2  4×3   ,   list
A=[[0]*3]*4
A
[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
#          
#         ,          ,         A  
A[0][1]=1
A
[[0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0]]
A[1][1]=0
A
[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]

2.サイクルによる初期値の付与
forループによりリストに値を割り当て,初期化要素ごとに0とする.この場合、方法1で発生する浅いコピーの問題は発生しません.
B=[[0 for t in range(2)]for i in range(3)]
B
[[0, 0], [0, 0], [0, 0]]
B[0][1]=1
B
[[0, 1], [0, 0], [0, 0]]

以上より,初期値をループ付与することは,オブジェクト参照の問題でバグが発生しない比較的推奨される初期化方法である.付与,浅いコピー,深いコピーについてはid()関数で実験を行い,深く理解できる.id()関数は、python解釈器内のオブジェクトのメモリアドレスを表すオブジェクトの一意の番号を返すことができる.詳細については、Pythonの深いコピーと浅いコピーの図解を参照してください.
#   1
A=[0]*4
print id(A)
A=[[0]*3]*4
print id(A)
A[0][1]=1
print id(A[0]),id(A[1])
#   2
B =[[0 for i in range(3)]for j in range(2)]
print id(B[0]),id(B[1])

結果
#   1            id,         
210971272 # id [0]*4
138267400 # id [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
138267208 138267208  #id(A[0]),id(A[1])
#   2               id
210971720 210871240