トホホな疑問(11) Python、空listの繰り返し

普段、あまりモノを考えずにPythonのコードを感覚的に書いているからだと思うのですが、時々、ハマります。今回は、便利な *演算子、掛け算だけでなく、文字列やリストにも使えるアレ、を考え無しに空リストに使ってハマった件を書かせていただきます。ま、後で見てみれば馬鹿馬鹿しい、けれども、ハマったときには見えていない。。。

※「トホホな疑問」投稿順Indexはこちら

以下の例は、例によって Raspbian上のPython 3.7.3で動かしております。

$ python3
Python 3.7.3 (default, Apr 3 2019, 05:39:12)
[GCC 8.2.0] on linux

さて、やりたかったことを簡単に書けばこんな感じです。

>>> a = [[], [], []]
>>> a[0].append(1)
>>> a
[[1], [], []]

ここではn=3ですが、n個の空リストを内部に含むリストを作って変数 a に代入しています。この書き方であれば、当然、上のようにある要素にappendするならば、ちゃんとその要素「だけ」にappendされます。ただ、n=1000みたいなとき, 空リストを1000個書くのは面倒です。そういうときのための

リスト内包表記

で書けば何の問題も無かったのです。こんな感じ。

>>> b = [ [] for i in range(3) ]
>>> b
[[], [], []]
>>> b[0].append(2)
>>> b
[[2], [], []]

しかし、つい魔が差しました。便利な * 演算子あったな、と。文字列に作用させれば繰り返しのある長い文字列を生成し、リストに作用させれば繰り返しのある長いリストを生成してくれます。こんな感じ。

>>> "-" * 50
'--------------------------------------------------'
>>> [1, 2] * 3
[1, 2, 1, 2, 1, 2]

ただ、単なる空リスト []に作用させても、空リストは空リストです。

>>> [] * 3
[]

もしやと思ってこう書きました。c と d 同じものに見えますか?

>>> c = [[]] * 3
>>> d = [[],[],[]]
>>> c
[[], [], []]
>>> d
[[], [], []]

見かけに騙されてると、私と同じところにハマります。c と d は違うです。論より証拠、要素にappendしてみます。

>>> c[0].append('c')
>>> d[0].append('d')
>>> c
[['c'], ['c'], ['c']]
>>> d
[['d'], [], []]

cの最初の要素にappendすると全ての要素が変化しています。ところがdの最初の要素にappendしても変化するのは最初の要素だけです。お分かりでしょう。cの場合は、「同じ空リスト」を並べているのでした。だから、オブジェクトのidをば眺めてみれば、皆同じ

>>> id(c[0])
1989530248
>>> id(c[1])
1989530248
>>> id(c[2])
1989530248

同じことをdで調べるならば、以下のように皆違います。

>> id(d[0])
1989530328
>>> id(d[1])
1989530368
>>> id(d[2])
1989530408

勿論、リスト内包表記によるのであれば、dと同様なリストが得られます。勿論、nが1000でも10000でもばっちこい。

>>> e = [ [] for i in range(1000)]
>>> id(e[0])
1989530488
>>> id(e[999])
1989554704

トホホな疑問(10) cuDNN、サンプル動かないのもある(Jetson nanoで)へ戻る

トホホな疑問(12) Python3移行、文字列数値変換にハマる へ進む