生成器中try-finally执行时机差异的原因咨询
关于ClosableQueue迭代器中finally块执行时机的疑问与解释
我正在学习《Effective Python》课程中用于同步并发操作的可关闭队列内容,遇到两段ClosableQueue类的__iter__方法实现代码:
第一段代码:
class ClosableQueue(Queue): SENTINEL = object() def close(self): self.put(self.SENTINEL) def __iter__(self): while True: try: item = self.get() if item is self.SENTINEL: return else: yield item finally: self.task_done()
这段代码里的finally块会在每次yield队列元素后执行。
第二段修改后的代码:
class ClosableQueue(Queue): SENTINEL = object() def close(self): self.put(self.SENTINEL) def __iter__(self): try: item = self.get() if item is self.SENTINEL: return else: yield item finally: self.task_done()
这里的finally块仅在迭代器耗尽后执行。
我初步理解为:生成器的finally块应在迭代器耗尽时执行,但第一段代码因while True循环使生成器持续yield未耗尽,故finally每次执行。请问该理解是否正确?
准确解释
你的理解核心方向是对的,需要结合生成器的执行特性做更精准的拆解:
- 生成器的暂停与恢复逻辑:生成器函数执行到
yield时会暂停,将值返回给调用者;下次迭代时从暂停位置继续执行。 - 第一段代码的执行流程:
while True让生成器持续循环,每次循环都会进入try块调用get()获取元素。- 如果取到的不是哨兵值,执行
yield后生成器暂停;当调用者发起下一次迭代时,生成器从暂停处恢复,直接进入finally块执行task_done(),随后回到while True开头重新进入try块。 - 当取到哨兵值时,执行
return终止生成器,此时也会先执行当前try对应的finally块。 - 本质是每次循环都会完整走完
try/finally流程,所以每次yield后都会触发finally执行。
- 第二段代码的执行流程:
try块仅在生成器启动时执行一次:获取一个元素,要么yield后暂停,要么遇到哨兵值直接return。- 若执行了
yield,生成器暂停,此时finally块不会执行;只有当生成器被终止(比如调用者停止迭代、遇到哨兵值return)时,才会触发finally块执行task_done()。 - 这段代码的
__iter__本质是单次迭代的生成器,只能返回一个元素,finally仅在整个迭代器生命周期结束时执行一次。
总结:第一段代码的finally属于循环内部的try块,每次循环迭代都会触发;第二段的finally属于生成器函数外层的try块,仅在生成器终止时执行一次。
内容的提问来源于stack exchange,提问作者Lak
相关产品推荐
相关产品推荐

