which one of these is better?

Fredrik Lundh fredrik at pythonware.com
Thu Oct 26 22:08:39 CEST 2006

John Salerno wrote:

>> Not sure about the with-- I just went to read the PEP on it, and it
>> confused me greatly.  :-)  So, I don't know.
> Ditto. :)

No need to be confused; the "with" statement is actually very simple. 
Consider this piece of code:

     set things up
         do something
         tear things down

If you do this a lot, it would quite convenient if you could put the 
"set things up" and "tear things down" code in a library function, so 
you could reuse it.  You can of course do something like

     def controlled_execution(callback):
         set things up
             tear things down

     def my_function(thing):
         do something


but that's a bit verbose, especially if you need to modify local 
variables.  Another approach is to use a one-shot generator, and
abuse the for-in statement:

     def controlled_execution():
         set things up
             yield thing
             tear things down

     for thing in controlled_execution():
         do something with thing

but this looks a bit weird, and doesn't even work in 2.4 and earlier. 
So after contemplating a number of alternatives, GvR finally came up 
with a generalization of the latter, using an object instead of a 
generator to control the behaviour of an external piece of code:

     class controlled_execution:
         def __enter__(self):
             set things up
             return thing
         def __exit__(self, type, value, traceback):
             tear things down

     with controlled_execution() as variable:
          some code

When the "with" statement is executed, Python evaluates the expression, 
calls the __enter__ method on the resulting value (which is called a 
"context guard"), and assigns whatever __enter__ returns to the given 
variable.  Python will then execute the code body, and *no matter what 
happens in that code*, call the guard object's __exit__ method.

For extra bonus, the __exit__ method can look at the exception, if any, 
and suppress or modify it, if necessary.  For example, the following 
__exit__ method swallows any TypeError, but lets all other exceptions 

     def __exit__(self, type, value, traceback):
         return isinstance(value, TypeError)

In Python 2.5, the file object has been equipped with __enter__ and 
__exit__ methods; the former simply returns the file object itself, and 
the latter closes the file:

     >>> f = open("x.txt")
     >>> f
     <open file 'x.txt', mode 'r' at 0x00AE82F0>
     >>> f.__enter__()
     <open file 'x.txt', mode 'r' at 0x00AE82F0>
     >>> f.read(1)
     >>> f.__exit__(None, None, None)
     >>> f.read(1)
     Traceback (most recent call last):
       File "<stdin>", line 1, in <module>
     ValueError: I/O operation on closed file

This wasn't very difficult, was it?


More information about the Python-list mailing list