Soruyu basitleştirelim. Tanımlamak:
def get_petters():
for animal in ['cow', 'dog', 'cat']:
def pet_function():
return "Mary pets the " + animal + "."
yield (animal, pet_function)
Sonra, aynı soruda olduğu gibi, şunu elde ederiz:
>>> 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.
Ancak bir list()
ilk oluşturmaktan kaçınırsak :
>>> for name, f in get_petters():
... print(name + ":", f())
cow: Mary pets the cow.
dog: Mary pets the dog.
cat: Mary pets the cat.
Neler oluyor? Bu ince fark neden sonuçlarımızı tamamen değiştiriyor?
Bakarsak list(get_petters())
, değişen bellek adreslerinden gerçekten üç farklı işlev verdiğimiz anlaşılıyor:
>>> 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>)]
Ancak, cell
bu işlevlerin bağlı olduğu 'lere bir göz atın :
>>> 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>,)
Her iki döngü için cell
nesne yinelemeler boyunca aynı kalır. Bununla birlikte, beklendiği gibi str
, ikinci döngüde referans verdiği spesifikler değişir. cell
Nesne belirtmektedir animal
zaman oluşturulduğu, get_petters()
olarak adlandırılır. Bununla birlikte, jeneratör işlevi çalışırken ifade ettiği nesneyi animal
değiştirir .str
İlk döngüde, her yineleme sırasında, tüm s'leri oluştururuz f
, ancak bunları yalnızca jeneratör get_petters()
tamamen tükendikten ve bir list
işlevler zaten oluşturulduktan sonra çağırırız .
İkinci döngüde, her yineleme sırasında, get_petters()
üreteci durduruyoruz ve f
her duraklamadan sonra arıyoruz . Böylelikle, animal
jeneratör fonksiyonunun duraklatıldığı o andaki değeri elde etmiş oluruz .
@Claudiu'nun benzer bir soruya yanıt verdiği gibi :
Üç ayrı işlev oluşturulur, ancak her biri tanımlandıkları ortamın kapanışına sahiptir - bu durumda, küresel çevre (veya döngü başka bir işlevin içine yerleştirilmişse dış işlevin ortamı). Yine de sorun tam olarak budur - bu ortamda, animal
mutasyona uğramıştır ve kapanışların tümü aynıdır animal
.
[Editör notu: i
olarak değiştirildi animal
.]
for animal in ['cat', 'dog', 'cow']
... Eminim birisi gelip bunu açıklayacaktır - bu Python gotcha'lardan biridir :)