はじめに
先日見かけたツイート。
Pythonで
— Hideyuki Tanaka (@tanakh) 2018年3月26日
> a = [[0]*3]*3
> a[0][0] = 1
が
> [[1,0,0],[0,0,0],[0,0,0]]
でも
> [[1,1,1],[1,1,1],[1,1,1]]
でもなく、
> [[1,0,0],[1,0,0],[1,0,0]]
になるのは最高にクッソって感じはする。
こうなる理由を解説します。
[0]
まず単に[0]
と書くとどういう意味なのか。図で書くとこういう意味です。
「こうじゃない」という図も書こうと思ったけどめんどくさいのでやめ。
つまり、[0]とは箱があってその中に0が入っているのではなく、箱の中には0という実体への参照情報が入ってます。
[0]*3
次に[0]*3とした場合にできるもの。
はいここ重要。1要素目、2要素目、3要素目は全て同じ0の実体を指します。
[[0]*3]*3
では問題の[[0]*3]*3。
3要素のリストで、それぞれが[0]*3というリストの実体を指すものが作られます。
a[0][0]=1
で、a[0][0]=1するとどうなるか。こうなります。
つまり、元々は3要素とも0を指していた内側のリストが先頭要素だけ1を指すようになります。
一方、外側のリストは実体が一つしかない内側のリストを各要素が指している状態のままです。というわけで、
[[1, 0, 0], [1, 0, 0], [1, 0, 0]]
となります。
まとめ
リストを*した場合、参照情報がコピーされます。指しているものがコピーされるわけではありません。ここら辺の挙動を理解しないでプログラム書くと不幸になるかもしれません。