Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

try-finally in Python 3 generator

I have met a snippet of Python 3 code:

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

print(next(gen()))

After I run it, I thought at first that the output should be:

1

But actually the result is:

stop
1

How can this happen? What happened under the hood?

If I run for i in gen(): print(i), there will be an infinite loop which is what I expected. What is the difference between for and next here?

like image 728
ruanhao Avatar asked May 09 '19 15:05

ruanhao


2 Answers

The finally clause is being executed on garbage collection of the generator object.

Consider the following two scenarios:

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
like image 108
Vince W. Avatar answered Nov 06 '22 19:11

Vince W.


The loop terminates when the generator is closed, which happens automatically if you don't save a reference to it. Once that happens, the try statement guarantees that the finally block is executed before the generator object is garbage collected. Compare:

>>> next(gen())
stop
1

with

>>> x = gen()
>>> next(x)
1
like image 31
chepner Avatar answered Nov 06 '22 17:11

chepner