普段、あまりモノを考えずに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