同じ値がN個入っているリストの作り方

Twitterより転載:

atsuoishimoto: .@t2y http://htn.to/qWhHgD [0 for i in xrange(len(seqs))] みたいな書き方たまに見かけるけど、正解は [0]*len(seqs) だと思う。

とても正しいツッコミだけど、若干不親切だと思うので勝手に補足。

たとえば「0がN個入っているリスト」を作るのは[0] * Nだ。

>>> N = 5
>>> [0] * N
[0, 0, 0, 0, 0]

この書き方は0に限らず「同じものがN個入ったリスト」を作ることに使える。

>>> ['a'] * N
['a', 'a', 'a', 'a', 'a']

じゃあ、「空のリストがN個入ったリスト」を作りたい場合は?うん、その場合ももちろん同じように書ける。

>>> [[]] * N
[[], [], [], [], []]

しかし、これが「同じ『空のリスト』がN個入ったリスト」であるということをきちんと理解しているかな?

>>> lists = [[]] * N
>>> lists[0] # listsの0番目は空のリストだ
[]
>>> lists[0].append(1) # これに1を追加すると
>>> lists[0] # [1]に変わる。うん当たり前。
[1]
>>> lists # で、listsはどうなっている?
[[1], [1], [1], [1], [1]]

listsは「同じ空リスト」が5つ入ったリストだったのだから当然こうなるわけである。でもまあ「リストのリスト」を作りたい時って普通はこういうことがしたいわけじゃないよね。「別々の空リスト」が5個入ったリストが欲しいんだよね。じゃあこうだ。

>>> [[] for i in range(N)]
[[], [], [], [], []]

これならば見かけは1つ目の方法で作ったリストと同じだけど、それぞれの空リストはそれぞれ別の空リストだ。

>>> lists = [[] for i in range(N)]
>>> lists[0].append(1)
>>> lists
[[1], [], [], [], []]

というわけで、例えば「要素が0の2次元配列を作りたい」という場合にはこうだ。

>>> from pprint import pprint
>>> pprint([[0] * N for i in range(N)])
[[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]]

多分最初に突っ込まれていた人は「同じ物」なのか「同じ値の別の物」なのかで使い分けが必要であることを理解していなかったのだろう。