理解に苦労しているPythonの動作に出くわしました。これは概念実証コードです。
from functools import partial
if __name__ == '__main__':
sequence = ['foo', 'bar', 'spam']
loop_one = lambda seq: [lambda: el for el in seq]
no_op = lambda x: x
loop_two = lambda seq: [partial(no_op, el) for el in seq]
for func in (loop_one, loop_two):
print [f() for f in func(sequence)]
上記の出力は次のとおりです。
['spam', 'spam', 'spam']
['foo', 'bar', 'spam']
の動作loop_one
は驚くべきものですloop_two
。 :el
は各ループで変化する不変の値 (文字列) ですが、ループがリサイクルする場合のようlambda
に、「ループ変数」へのポインターを格納しているようです。シーケンスの各要素に対して同じメモリ アドレス。
上記の動作は、 for ループを含む本格的な関数と同じです (したがって、リスト内包表記ではありません)。
しかし、待ってください: まだまだあります...もっと不可解です!
次のスクリプトは次のように機能しますloop_one
。
b = []
for foo in ("foo", "bar"):
b.append(lambda: foo)
print [a() for a in b]
(出力: ['bar', 'bar']
)
foo
しかし、変数名を に置き換えるとどうなるか見てみましょうa
:
b = []
for a in ("foo", "bar"):
b.append(lambda: a)
print [a() for a in b]
(出力: [<function <lambda> at 0x25cce60>, <function <lambda> at 0x25cced8>]
)
ここで何が起こっているのか分かりますか? インタープリターの基になる C 実装に関連する落とし穴があるに違いないと思いますが、この動作が異なる実装間で一貫しているかどうかをテストするもの (Jthon、PyPy など) は他にありません。