Python 3生成器中的try-finally

ftf50wuq  于 2023-02-06  发布在  Python
关注(0)|答案(3)|浏览(222)

我遇到了Python 3代码的一个片段:

def gen():
    try:
        while True:
            yield 1
    finally:
        print("stop")

print(next(gen()))

在我运行它之后,我起初以为输出应该是:
但实际上结果是:

stop
1

怎么会这样?引擎盖下发生了什么?
如果我运行for i in gen(): print(i),将会出现一个无限循环,这正是我所期望的。这里fornext的区别是什么?

dw1jzc5e

dw1jzc5e1#

正在对生成器对象的垃圾回收执行finally子句。
请考虑以下两种情况:

def gen():
    try:
        while True:
            yield 1
    finally:
        print("stop")

g1 = gen(); print('first time')
print(next(g1))
g2 = gen(); print('second time')  # no stop will be printed because we haven't hit the finally clause yet
def gen():
    try:
        while True:
            yield 1
    finally:
        print("stop")

g = gen(); print('first time')
print(next(g))
g = gen(); print('second time')   # stop will be printed when the first object g was assigned to is garbage collected
g9icjywg

g9icjywg2#

生成器关闭时循环终止,如果不保存对它的引用,则会自动关闭。一旦关闭,try语句保证finally块在生成器对象被垃圾回收之前执行。

>>> next(gen())
stop
1

>>> x = gen()
>>> next(x)
1
5rgfhyps

5rgfhyps3#

我在为未来的读者回答一个老问题。
生成器中的finally子句将在解释器在生成器内部执行代码时到达它时执行,或者在解释器在生成器内部执行代码时没有到达它的情况下,在生成器被终止(删除)时执行。您可以阅读引用。(请参阅以"Yield expressions are allowed anywhere"开头的段落。)
下面的示例演示了这一点。

def gen(name):
    try:
        yield None
        print('after yield', name)
    finally:
        print('after finally', name)

g1 = gen('g1')
next(g1)
next(g1, None)
print('before del g1')
del g1

g2 = gen('g2')
next(g2)
print('before del g2')
del g2

这将输出以下内容。

after yield g1
after finally g1
before del g1
before del g2
after finally g2

对于for循环的第二个问题,我将给出一个示例。

for e in gen():
    print(e)

上面的代码等效于下面的代码。

iterator = gen()
while True:
    try:
        e = next(iterator)
    except StopIteration:
        break
    print(e)
del iterator

相关问题