มาทำให้คำถามง่ายขึ้น กำหนด:
def get_petters():
for animal in ['cow', 'dog', 'cat']:
def pet_function():
return "Mary pets the " + animal + "."
yield (animal, pet_function)
จากนั้นเช่นเดียวกับในคำถามเราได้รับ:
>>> 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.
แต่ถ้าเราหลีกเลี่ยงการสร้างสิ่งlist()แรก:
>>> for name, f in get_petters():
... print(name + ":", f())
cow: Mary pets the cow.
dog: Mary pets the dog.
cat: Mary pets the cat.
เกิดอะไรขึ้น? เหตุใดความแตกต่างที่ลึกซึ้งนี้จึงเปลี่ยนผลลัพธ์ของเราอย่างสิ้นเชิง
ถ้าเราดูlist(get_petters())มันชัดเจนจากที่อยู่หน่วยความจำที่เปลี่ยนไปซึ่งเราให้ฟังก์ชันที่แตกต่างกันสามอย่าง:
>>> 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>)]
อย่างไรก็ตามลองดูที่cellฟังก์ชันเหล่านี้เชื่อมโยงกับ:
>>> 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>,)
สำหรับทั้งสองลูปcellวัตถุจะยังคงเหมือนเดิมตลอดการวนซ้ำ อย่างไรก็ตามตามที่คาดstrไว้การอ้างอิงที่เฉพาะเจาะจงจะแตกต่างกันไปในลูปที่สอง cellวัตถุหมายถึงanimalซึ่งถูกสร้างขึ้นเมื่อget_petters()ถูกเรียกว่า อย่างไรก็ตามanimalการเปลี่ยนแปลงสิ่งที่strวัตถุมันหมายถึงเป็นฟังก์ชั่นเครื่องกำเนิดไฟฟ้าวิ่ง
ในลูปแรกระหว่างการวนซ้ำแต่ละครั้งเราสร้างfs ทั้งหมดแต่เราจะเรียกมันหลังจากที่เครื่องกำเนิดไฟฟ้าget_petters()หมดลงอย่างสมบูรณ์และlistฟังก์ชันถูกสร้างขึ้นแล้ว
ในลูปที่สองระหว่างการวนซ้ำแต่ละครั้งเรากำลังหยุดget_petters()เครื่องกำเนิดไฟฟ้าชั่วคราวและเรียกใช้fหลังจากหยุดแต่ละครั้ง ดังนั้นเราจึงดึงค่าของanimalช่วงเวลานั้นในช่วงเวลาที่ฟังก์ชันเครื่องกำเนิดไฟฟ้าหยุดชั่วคราว
ดังที่ @Claudiu ให้คำตอบสำหรับคำถามที่คล้ายกัน :
มีการสร้างฟังก์ชันแยกกันสามฟังก์ชัน แต่แต่ละฟังก์ชันมีการปิดสภาพแวดล้อมที่กำหนดไว้ในกรณีนี้คือสภาวะแวดล้อมส่วนกลาง (หรือสภาพแวดล้อมของฟังก์ชันภายนอกหากวางลูปไว้ในฟังก์ชันอื่น) ตรงนี้เป็นปัญหาที่เกิดขึ้นแม้ว่า - ในสภาพแวดล้อมนี้กลายพันธุ์และการปิดทั้งหมดให้ดูที่เดียวกันanimalanimal
[หมายเหตุบรรณาธิการ: iถูกเปลี่ยนเป็นanimal.]
for animal in ['cat', 'dog', 'cow']... ฉันแน่ใจว่าจะมีคนมาอธิบายเรื่องนี้ - มันเป็นหนึ่งใน Python gotcha ของ :)