リスト内包表記の評価タイミング
Pythonにはリスト内包表記というのがあり、リストなどの全ての要素に処理を施したい場合、1行で簡潔に書ける。
array = [0, 1, 2] array2 = [x + 1 for x in array] # <-これ print(array2) # => [1, 2, 3] # 処理的にはこれと一緒 array3 = [] for x in array: array3.append(x + 1) print(array3) # => [1, 2, 3]
このarray、どのタイミングで評価されるかというと、通常のfor文と一緒で、ループのたびにarrayを参照して、逐次取り出される。
いや、当たり前っちゃ当たり前なのだが……
たとえばlist.extend()を使うとき。list.extend(iterable)は、iterableでlistを拡張する。
array1 = [0, 1, 2] array2 = [3, 4, 5] array1.extend(array2) # => [0, 1, 2, 3, 4, 5]
iterableに「拡張元のlistのリスト内包表記」を用いる場合、listはループのたびに順次拡張される。結果、リスト内包表記がいつまでも終端に達せず、無限ループに陥る。
array = [0, 1, 2] array.extend(x + 1 for x in array) # [0,1,2,1,2,3]になるのではなく、 # arrayはループのたびに順次拡張され、無限ループに陥ってメモリを食いつぶして死ぬ # [0,1,2,1,2,3,2,3,4,3,4,5,...] for x in array: array.append(x + 1) # 要はこれと同じ。これも無限ループに陥る。 array.extend([x + 1 for x in array]) # => [0, 1, 2, 1, 2, 3] # 渡す前にリストにしてしまえば、もうarrayのiterationは完了しているので、大丈夫
listの拡張元に同じlistを使うことはあまり無いものの、忘れた頃についやってしまいがちなミス。しかもメモリが死ぬので何もできなくなってやばい。