Mari sederhanakan pertanyaannya. Menetapkan:
def get_petters():
for animal in ['cow', 'dog', 'cat']:
def pet_function():
return "Mary pets the " + animal + "."
yield (animal, pet_function)
Kemudian, seperti dalam pertanyaan, kita mendapatkan:
>>> 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.
Tetapi jika kita menghindari membuat yang list()
pertama:
>>> for name, f in get_petters():
... print(name + ":", f())
cow: Mary pets the cow.
dog: Mary pets the dog.
cat: Mary pets the cat.
Apa yang sedang terjadi? Mengapa perbedaan halus ini benar-benar mengubah hasil kami?
Jika kita lihat list(get_petters())
, jelas dari alamat memori yang berubah bahwa kita memang menghasilkan tiga fungsi yang berbeda:
>>> 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>)]
Namun, perhatikan cell
bahwa fungsi-fungsi ini terikat:
>>> 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>,)
Untuk kedua loop, cell
objek tetap sama selama iterasi. Namun, seperti yang diharapkan, str
referensi spesifik itu bervariasi di loop kedua. The cell
objek mengacu pada animal
, yang dibuat ketika get_petters()
disebut. Namun, animal
mengubah apastr
objek yang dirujuknya saat fungsi generator berjalan .
Di loop pertama, selama setiap iterasi, kami membuat semua file f
s, tetapi kita hanya memanggilnya setelah generator get_petters()
benar-benar habis dan a list
dari fungsi sudah dibuat.
Di loop kedua, selama setiap iterasi, kami menjeda file get_petters()
generator dan memanggil f
setelah setiap jeda. Jadi, kami akhirnya mengambil nilai animal
pada saat fungsi generator dihentikan sementara.
Seperti yang @Claudiu berikan untuk menjawab pertanyaan serupa :
Tiga fungsi terpisah dibuat, tetapi masing-masing memiliki penutupan lingkungan tempat mereka didefinisikan - dalam hal ini, lingkungan global (atau lingkungan fungsi luar jika loop ditempatkan di dalam fungsi lain). Namun, inilah masalahnya - di lingkungan ini,animal
bermutasi, dan closure semuanya mengacu pada hal yang sama animal
.
[Catatan editor: i
telah diubah menjadi animal
.]
for animal in ['cat', 'dog', 'cow']
... Saya yakin seseorang akan datang dan menjelaskan hal ini - ini salah satu dari Python gotcha :)