イヌツムリのメモ

学習したことのメモである。しかし、他の人が読んでもわかるように書こう。

pythonの多次元リストの罠にハマった

はじめに

久しぶりにpythonを触ったら、ハマった。
多次元リストを生成して、そのうちの1つ値を変更すると、 他の列の値も変更されてしまった。
初期化方法がまずく、1つの列をコピーして多次元リストとしていたことが原因だった。
リスト内包表記すれば、それぞれの列を別のオブジェクトとして生成してくれるので同じ問題が発生しない。
(そういえばそうやった...)

ハマった例

よくあるリストの初期化方法として下記の方法がある。

dp = [0] * 3
print(dp)

# [0, 0, 0]

dp[0] = 1

# [1, 0, 0]

しかし、これがハマりどころで、同じようにして3 * 3のリストを生成すると、
[0] * 3のリストをコピーして多次元リストを生成してしまう。
それにより、[0][0]の値を変更したつもりが他の列の値も変更してしまう。

dp = [ [0] * 3 ] * 3
print(dp)

# [ [0, 0, 0] , [0, 0, 0], [0, 0, 0] ]

dp[0][0] = 1

# [ [1, 0, 0] , [1, 0, 0], [1, 0, 0] ]

解決策

多次元リストを生成するときは、下記のようにリスト内包表記を用いると リストオブジェクトをコピーしないため、トラブルにならない。

dp = [ [0] * 3 for _ in range(3) ] 
print(dp)

# [ [0, 0, 0] , [0, 0, 0], [0, 0, 0] ]

dp[0][0] = 1

# [ [1, 0, 0] , [0, 0, 0], [0, 0, 0] ]

おわりに

[0] * 3 で、0番目の値が1番目や2番目にコピーされているけど、 [ [0, 0, 0] ] * 3ではリスト[0, 0, 0]のアドレスがコピーされているということがわかった。
pythonで何が値で、何がアドレスなのか上手に隠蔽してもらっているためにハマったようだ。
アドレスを意識するような言語を触っているから納得感あるが、 アドレスの概念がない人に教えるのが大変そうだ。