二次元リストを作る際の注意点
皆さん、二次元リストを書く際、どのように記述していますか?
恐らく多くの人が以下のようにfor文を用いて記述していると思います。
fields=[[0 for _ in range(4)] for _ in range(4)]
print(fields)
# [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
ただ、二次元リストを作るだけなら以下のようにも記述できますよね。
fields=[[0]*4]*4
print(fields)
# [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
リストの中身にもよりますが、状況によっては下のコードの方が、短く済みそうですよね。
しかし、実は下のコードは二次元リストとしての役割を果たしていません。
試しにリストの中身を更新してみます。
まずは、for文で書いた二次元リストから更新します。
fields=[[0 for _ in range(4)] for _ in range(4)]
print(fields)
# [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
fields[0][1]=1
print(fields)
# [[0, 1, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
次に、「*」を使ったリストを更新してみます。
fields=[[0]*4]*4
print(fields)
# [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
fields[0][1]=1
print(fields)
# [[0, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0]]
指定したのは[0][1]のみなのに、実際には[0:][1]が更新されています。
理由は、オブジェクトのidにあります。
fields=[[0 for _ in range(4)] for _ in range(4)]
print(fields)
# [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
print(id(fields[0]))
print(id(fields[1]))
# 2754287228736
# 2754287228864
本来は、このように同じ二次元リストに存在していても行ごとにidが違うはずです。
では、「*」の二次元リストはどうでしょうか。
fields=[[0]*4]*4
print(fields)
# [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
print(id(fields[0]))
print(id(fields[1]))
# 2754288924672
# 2754288924672
同じidが出力されています。
普段、私たちが変数を使う場合、コンピュータ側はidで指定しています。
なので、今回のような現象が起きてしまうわけです。
id関連の落とし穴はよくあるので、頭の片隅にでも置いていただければと思います。