質問を単純化しましょう。定義:
def get_petters():
for animal in ['cow', 'dog', 'cat']:
def pet_function():
return "Mary pets the " + animal + "."
yield (animal, pet_function)
次に、質問と同様に、次のようになります。
>>> for name, f in list(get_petters()):
... print(name + ":", f())
cow: Mary pets the cat.
dog: Mary pets the cat.
cat: Mary pets the cat.
しかし、list()
最初のものを作成しない場合:
>>> for name, f in get_petters():
... print(name + ":", f())
cow: Mary pets the cow.
dog: Mary pets the dog.
cat: Mary pets the cat.
どうしたの?このわずかな違いが結果を完全に変えるのはなぜですか?
を見るとlist(get_petters())
、メモリアドレスの変更から、3つの異なる関数が実際に生成されていることがわかります。
>>> list(get_petters())
[('cow', <function get_petters.<locals>.pet_function at 0x7ff2b988d790>),
('dog', <function get_petters.<locals>.pet_function at 0x7ff2c18f51f0>),
('cat', <function get_petters.<locals>.pet_function at 0x7ff2c14a9f70>)]
ただし、cell
これらの関数がバインドされているsを見てください。
>>> for _, f in list(get_petters()):
... print(f(), f.__closure__)
Mary pets the cat. (<cell at 0x7ff2c112a9d0: str object at 0x7ff2c3f437f0>,)
Mary pets the cat. (<cell at 0x7ff2c112a9d0: str object at 0x7ff2c3f437f0>,)
Mary pets the cat. (<cell at 0x7ff2c112a9d0: str object at 0x7ff2c3f437f0>,)
>>> for _, f in get_petters():
... print(f(), f.__closure__)
Mary pets the cow. (<cell at 0x7ff2b86b5d00: str object at 0x7ff2c1a95670>,)
Mary pets the dog. (<cell at 0x7ff2b86b5d00: str object at 0x7ff2c1a952f0>,)
Mary pets the cat. (<cell at 0x7ff2b86b5d00: str object at 0x7ff2c3f437f0>,)
両方のループで、cell
オブジェクトは反復全体を通じて同じままです。ただし、予想どおり、str
2番目のループではそれが参照する特定の情報が異なります。cell
オブジェクトが参照するanimal
ときに作成された、get_petters()
と呼ばれています。ただし、ジェネレーター関数の実行時に参照するオブジェクトをanimal
変更しstr
ます。
最初のループでは、各反復中にすべてのを作成しますf
が、ジェネレーターget_petters()
が完全に使い果たされ、list
関数のがすでに作成された後でのみ、それらを呼び出します。
2番目のループでは、各反復中にget_petters()
ジェネレーターを一時停止し、f
各一時停止後に呼び出します。したがって、animal
ジェネレータ関数が一時停止しているその瞬間の値を取得することになります。
@Claudiuが同様の質問に答えるように:
3つの個別の関数が作成されますが、それぞれが定義されている環境(この場合は、グローバル環境(またはループが別の関数内に配置されている場合は外部関数の環境))のクロージャーを持っています。ただし、これはまさに問題です。この環境でanimal
は変更され、すべてのクロージャが同じものを参照しanimal
ます。
[編集者注:i
に変更されましたanimal
。]
for animal in ['cat', 'dog', 'cow']
...誰かがやって来てこれを説明すると確信しています-それはそれらのPythonの落とし穴の1つです:)