BETA

初心者がつまづいた、ぱっと見ではわからないリスト作成の落とし穴[Python3]

投稿日:2019-11-07
最終更新:2019-11-07

H x W の二次元リストをつくりたいとして、2通り示します

1

h, w = 5, 5  
table = [[0] * w] * h  

2

h, w = 5, 5  
table = [[0 for _ in range(w)] for _ in range(h)]  

上の二つを見て、違いが理解できますか?
もし説明できるのだとしたら、この先を読む必要はないです。

で、違いですがこの時点ではまだ判断できません。(あくまで見た目では)
実際に二つのtableを出力すると、どちらも以下のようになります
1

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

2

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



ではこうしたらどうなるでしょう?

table[0][0] = 1  

普通に考えればtableの0番目のリストの0番目の要素を1に更新したように思えます。
実査にtableを出力してみましょう
1

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

2

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

見ての通り、結果は異なります。
もちろん正しいのは(正しいと思うのは)2の方ですが実際にはどちらも正しいのです。


ではここで違いを確認するために、最初に戻ってみましょう

h, w = 5, 5  
table = [[0] * w] * h  

まず1の方法ですが、これは[0]というリストオブジェクトをW倍してつなげたものであり、そうしてできたリストをさらにH倍してtableが作られています。
python3 でいうところの'倍'というのは、要するに'同じものを倍'にするということであり、table[0][0] としたときに最初の0という指定をすることでpython プログラムがidによってlist objectを探そうとする時、tableの中の5つのリストは全て同じメモリーに格納されています。つまり、このとき
table[0][0], table[1][0], ... table[4][0]は(単に同じ値とういことではなく)すべて全く同じidを持つobject、ということです。
これについてはlist2 = list1list2 = list1.copy() の違いを調べてもらえれば理解できるかと思います
で、結局のところ、interpreter からみてtable[0][0]というのは5つのリストすべての0番目のオブジェクトをさすわけなので、このような結果になるのです。
(p.s.この全く同じものが同時に別の場所に存在するみたいなの、なんか量子力学っぽいですよね)

h, w = 5, 5  
table = [[0 for _ in range(w)] for _ in range(h)]  

対して2の方法では、5回ループで毎回新しいリストのインスタンスを作成して、その中でさらに5 回ループで毎回、0 という数値0を作り出すクラスメソッドのaliasを使って数値の0を戻り値として返しています。なので全ての 0 はidレベルで全く別物というわけです


ここまで読めばなんとなく違いが理解できたかと思います。
私も最初出力結果がおかしくて、最初バグだと思っていたのですが、ふとこの事実に気がつくまでに1週間くらいかかりました。
1次元リストなら1の方法が簡単でいいですが、二次元以上のリストを作成する時は特別な理由がない限り2の方法を使うと良さそうです。
読んでいただきありがとうございました。

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
or 外部アカウントではじめる
10秒で技術ブログが作れます!