Python

リストを*したときの挙動

はじめに

先日見かけたツイート。

こうなる理由を解説します。

[0]

まず単に[0]と書くとどういう意味なのか。図で書くとこういう意味です。

one_elem.png

「こうじゃない」という図も書こうと思ったけどめんどくさいのでやめ。
つまり、[0]とは箱があってその中に0が入っているのではなく、箱の中には0という実体への参照情報が入ってます。

[0]*3

次に[0]*3とした場合にできるもの。

tree_elems.png

はいここ重要。1要素目、2要素目、3要素目は全て同じ0の実体を指します。

[[0]*3]*3

では問題の[[0]*3]*3。

three_lists.png

3要素のリストで、それぞれが[0]*3というリストの実体を指すものが作られます。

a[0][0]=1

で、a[0][0]=1するとどうなるか。こうなります。

point_other.png

つまり、元々は3要素とも0を指していた内側のリストが先頭要素だけ1を指すようになります。
一方、外側のリストは実体が一つしかない内側のリストを各要素が指している状態のままです。というわけで、

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

となります。

まとめ

リストを*した場合、参照情報がコピーされます。指しているものがコピーされるわけではありません。ここら辺の挙動を理解しないでプログラム書くと不幸になるかもしれません。