eom
eom

Reputation: 61

Will the generator be closed automatically after fully iteration?

Do I have to write

def count10():
    for i in range(10):
        yield i
gen = count10()
for j in gen:
    print(j)
gen.close()

to save memory, or just

def count10():
    for i in range(10):
        yield i
for j in count10():
    print(j)

In fact I would like to learn details of lifecycle of Python generator but failed to find relevant resources.

Upvotes: 2

Views: 1019

Answers (2)

Booboo
Booboo

Reputation: 44108

The close method for generators came about in PEP 342:

Add a close() method for generator-iterators, which raises GeneratorExit at the point where the generator was paused. If the generator then raises StopIteration (by exiting normally, or due to already being closed) or GeneratorExit (by not catching the exception), close() returns to its caller. If the generator yields a value, a RuntimeError is raised. If the generator raises any other exception, it is propagated to the caller. close() does nothing if the generator has already exited due to an exception or normal exit.

Note the last sentence: close() does nothing if the generator has already exited due to an exception or normal exit.

Upvotes: 3

user2357112
user2357112

Reputation: 280380

You don't need to close that generator.

close-ing a generator isn't about saving memory. (close-ing things is almost never about saving memory.) The idea behind the close method on a generator is that you might stop iterating over a generator while it's still in the middle of a try or with:

def gen():
    with something_important():
        yield from range(10)

for i in gen():
    if i == 5:
        break

close-ing a suspended generator throws a GeneratorExit exception into the generator, with the intent of triggering finally blocks and context manager __exit__ methods. Here, close would cause the generator to run the __exit__ method of something_important(). If you don't abandon a generator in the middle like this (or if your generator doesn't have any finally or with blocks, including in generators it delegates to with yield from), then close is unnecessary (and does nothing).

The memory management system usually runs close for you, but to really ensure prompt closure across Python implementations, you'd have to replace code like

for thing in gen():
    ...

with

with contextlib.closing(gen()) as generator:
    for thing in generator:
        ...

I've never seen anyone do this.

Upvotes: 7

Related Questions