Uprośćmy pytanie. Definiować:
def get_petters():
for animal in ['cow', 'dog', 'cat']:
def pet_function():
return "Mary pets the " + animal + "."
yield (animal, pet_function)
Następnie, tak jak w pytaniu, otrzymujemy:
>>> 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.
Ale jeśli unikniemy tworzenia list()pierwszego:
>>> for name, f in get_petters():
... print(name + ":", f())
cow: Mary pets the cow.
dog: Mary pets the dog.
cat: Mary pets the cat.
Co się dzieje? Dlaczego ta subtelna różnica całkowicie zmienia nasze wyniki?
Jeśli się przyjrzymy list(get_petters()), ze zmieniających się adresów pamięci jasno wynika, że rzeczywiście dajemy trzy różne funkcje:
>>> 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>)]
Jednak spójrz na elementy, cellktóre te funkcje są powiązane:
>>> 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>,)
W przypadku obu pętli cellobiekt pozostaje taki sam przez wszystkie iteracje. Jednak, zgodnie z oczekiwaniami, specyfika, do strktórej się odnosi, różni się w drugiej pętli. cellObiektu odnosi się do animal, który tworzy się, gdy get_petters()jest tzw. Jednak animalzmienia strobiekt, do którego się odnosi, gdy działa funkcja generatora .
W pierwszej pętli podczas każdej iteracji tworzymy wszystkie fs, ale wywołujemy je dopiero po get_petters()całkowitym wyczerpaniu generatora i utworzeniu jednej listz funkcji.
W drugiej pętli, podczas każdej iteracji, zatrzymujemy get_petters()generator i dzwonimy fpo każdej przerwie. W ten sposób otrzymujemy wartość animalw tym momencie, w którym funkcja generatora jest wstrzymana.
Jak @Claudiu odpowiada na podobne pytanie :
Tworzone są trzy oddzielne funkcje, ale każda z nich ma zamknięcie środowiska, w którym została zdefiniowana - w tym przypadku środowisko globalne (lub środowisko funkcji zewnętrznej, jeśli pętla jest umieszczona wewnątrz innej funkcji). W tym jednak dokładnie tkwi problem - w tym środowisku animaljest zmutowany, a wszystkie zamknięcia odnoszą się do tego samego animal.
[Uwaga redaktora: izmieniono na animal.]
for animal in ['cat', 'dog', 'cow']... Jestem pewien, że ktoś przyjdzie i to wyjaśni - to jeden z tych Pythona gotcha :)