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 cell
s 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 cell
bleibt das Objekt während der Iterationen gleich. Wie erwartet str
variiert die spezifische Referenz in der zweiten Schleife. Das cell
Objekt bezieht sich auf animal
das Objekt , das beim get_petters()
Aufruf erstellt wird. Jedoch animal
ändert sich, was str
Objekt , das es sich bezieht , wie die Generatorfunktion läuft .
In der ersten Schleife erstellen wir während jeder Iteration alle f
s, rufen sie jedoch erst auf, wenn der Generator get_petters()
vollständig erschöpft ist und bereits eine list
der Funktionen erstellt wurde.
In der zweiten Schleife halten wir während jeder Iteration den get_petters()
Generator an und rufen f
nach jeder Pause auf. Auf diese Weise erhalten wir den Wert animal
zu 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 animal
ist es mutiert und die Verschlüsse beziehen sich alle auf dasselbe animal
.
[Anmerkung des Herausgebers: i
wurde 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 :)