Perhaps someone who knows more about the internals of python and the recent changes in 2.6 and 2.7 can explain this question that came up today in a code review.

I suggest using with instead of try: ... finally: to close a file that was written to. Instead of this:


dest = file('foo', 'w')
try:
   dest.write('stuff')
finally:
   dest.close()
print open('foo').read()  # will print 'stuff'

We can use this:


with file('foo', 'w') as dest: 
    dest.write('stuff')
print open('foo').read()  # will print 'stuff'

Why does that work? I'm guessing it's because the file() instance object has a built in __exit__ method. Is that right?

That means I don't need to use contextlib.closing(thing) right?

For example, suppose you have this class:


class Farm(object):
   def __enter__(self):
       print "Entering"
       return self
   def __exit__(self, err_type, err_val, err_tb):
       print "Exiting", err_type
       self.close()
   def close(self):
       print "Closing"

with Farm() as farm:
   pass
# this will print:
#   Entering
#   Exiting None
#   Closing

Another way to achieve the same specific result would be to use the closing() decrorator:


class Farm(object):
   def close(self):
       print "Closing"

from contextlib import closing
with closing(Farm()) as farm:
   pass
# this will print:
#   Closing

So the closing() decorator "steals" the __enter__ and __exit__. This last one can be handy if you do this:


from contextlib import closing
with closing(Farm()) as farm:
   raise ValueError

# this will print
#  Closing
#  Traceback (most recent call last):
#   File "dummy.py", line 16, in <module>
#     raise ValueError
#  ValueError

This is turning into my own loud thinking and I think I get it now. contextlib.closing() basically makes it possible to do what I did there with the __enter__ and __exit__ and it seems the file() built-in has a exit handler that takes care of the closing already so you don't have to do it with any extra decorators.

Comments

Nick Coghlan

Correct, contextlib.closing() is just an adapter that maps the context management protocol (i.e. __enter__/__exit__) to any object with a close() method. It's most useful when you can't readily alter the original definition of the objects involved.

If an object already supports the context management protocol directly (which includes most* file-like objects in the standard library), then you don't need the adapter.

(*if we've missed any, then feature requests to fix that are usually looked on quite favourably)

Marcin Jaworski

You can also do the same with locks as Lock class also implements context management protocol (it acquires lock on __enter__ and releases it on __exit__)

from threading import Lock
lock = Lock()
def somefunc():
....with lock:
........do_something_within_lock()

(sorry, I had to use dots instead of spaces to indent code because I don't know how to mark it as a code in comment here)

You can always be sure, that the code inside __exit__ method will always be invoked (just like with try-except-finally).

Your email will never ever be published.

Related posts