Vereinfachen wir die Frage. Definieren:
def get_petters():
for animal in ['cow', 'dog', 'cat']:
def pet_function():
return "Mary pets the " + animal + "."
yield (animal, pet_function)
Dann bekommen wir genau wie in der Frage:
>>> 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.
Aber wenn wir es vermeiden, eine list()erste zu erstellen :
>>> for name, f in get_petters():
... print(name + ":", f())
cow: Mary pets the cow.
dog: Mary pets the dog.
cat: Mary pets the cat.
Was ist los? Warum verändert dieser subtile Unterschied unsere Ergebnisse vollständig?
Wenn wir uns das ansehen list(get_petters()), wird aus den sich ändernden Speicheradressen deutlich, dass wir tatsächlich drei verschiedene Funktionen liefern:
>>> 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>)]
Schauen Sie sich jedoch die cells an, an die diese Funktionen gebunden sind:
>>> 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>,)
Für beide Schleifen cellbleibt das Objekt während der Iterationen gleich. Wie erwartet strvariiert die spezifische Referenz in der zweiten Schleife. Das cellObjekt bezieht sich auf animaldas Objekt , das beim get_petters()Aufruf erstellt wird. Jedoch animaländert sich, was strObjekt , das es sich bezieht , wie die Generatorfunktion läuft .
In der ersten Schleife erstellen wir während jeder Iteration alle fs, rufen sie jedoch erst auf, wenn der Generator get_petters()vollständig erschöpft ist und bereits eine listder Funktionen erstellt wurde.
In der zweiten Schleife halten wir während jeder Iteration den get_petters()Generator an und rufen fnach jeder Pause auf. Auf diese Weise erhalten wir den Wert animalzu dem Zeitpunkt, zu dem die Generatorfunktion angehalten wurde.
Wie @Claudiu eine Antwort auf eine ähnliche Frage gibt :
Es werden drei separate Funktionen erstellt, die jedoch jeweils die Umgebung schließen, in der sie definiert sind - in diesem Fall die globale Umgebung (oder die Umgebung der äußeren Funktion, wenn die Schleife in einer anderen Funktion platziert ist). Dies ist jedoch genau das Problem - in dieser Umgebung animalist es mutiert und die Verschlüsse beziehen sich alle auf dasselbe animal.
[Anmerkung des Herausgebers: iwurde geändert in animal.]
for animal in ['cat', 'dog', 'cow']... Ich bin sicher, jemand wird mitkommen und dies erklären - es ist eines dieser Python-Fallstricke :)