Closing generators

Thomas Rachel nutznetz-0c1b6768-bfa9-48d5-a470-7603bd3aa915 at spamschutz.glglgl.de
Sat Apr 23 23:18:40 EDT 2011


Am 23.04.2011 04:15, schrieb Terry Reedy:

> .close() methods that release operating system resources are needed
> *because* there is no guarantee of immediate garbage collection. They
> were not needed when CPython was the only Python. The with statement was
> added partly to make it easier to make sure that .close() was called.

I was already aware that "with:" saves me a "finally: close(...)" and 
replaces the "try:" line by a slightly more complicated line, but seen 
on the whole it is much better memorizable.

I really like "with" and use it wherever sensible and practical.


>> But in these (admittedly rarely) cases, it is better practice to close
>> explicitly, isn't it?
>
> If by 'rare case' you mean a generator that opens a file or socket, or
> something similar, then yes. One can think of a opened file object as an
> iterator (it has __iter__ and __next__) with lots of other methods.

Oh, ok. I never thought about it in that way - and it is not exactly the 
same: a file object already has its __enter__ and __exit__ methods which 
serves to avoid using closing(), while a generator object misses them.

But thanks for pointing out that it is only necessary in generators who 
do "necessary" cleanup things while reacting to GeneratorExit or in 
their finally. Then a programmer should propagate exactly such 
generators (or their generating functions) not "as they are", but as 
context manager objects which yield a generator which is to be closed 
upon exit[1].

> Instead of writing such a generator, though, I might consider an
> iterator class with __enter__ and __exit__ methods so that it was also a
> with-statement context manager, just like file objects and other such
> things, so that closing would be similarly automatic. Or easier:
>
> from contextlib import closing

Yes, I mentionned that in the original posting, and I think my question 
is answered as well now - unlike file objects, generators were not 
designed to have a __enter__/__exit__ by themselves, but instead require 
use of closing() probably due to the fact that this feature is needed so 
rarely (although it would have been nice to have it always...).


Thanks for the answers,

Thomas

[1] Maybe in this way:

class GeneratorClosing(object):
     """Take a parameterless generator functon and make it a context
     manager which yields a iterator on each with: invocation. This
     iterator is supposed to be used (once, clearly) inside the with:
     and automatically closed on exit. Nested usage is supported as
     well."""
     def __init__(self, gen):
         self._iter = gen
         self._stack = []
     def __enter__(self):
         it = iter(self._iter())
         self._stack.append(it) # for allowing nested usage...
         return it
     def __exit__(self, *e):
         self._stack.pop(-1).close() # remove and close last element.

(which even can be used as a decorator to such a generator function, as 
long as it doesn't take arguments).

So I can do

@GeneratorClosing
def mygen():
     try:
         yield 1
         yield 2
     finally:
         print "cleanup"

and then use

with mygen as it:
     for i in it: print i
# Now, have the __enter__ call the generator function again in order to
# produce a new generator.
with mygen as it:
     for i in it: print i

and be sure that cleanup happens for every generator created in that way.



More information about the Python-list mailing list