Understanding the Python with statement and context managers
with
doesn't really replace try
/except
, but, rather, try
/finally
. Still, you can make a context manager do something different in exception cases from non-exception ones:
class Mgr(object):
def __enter__(self): pass
def __exit__(self, ext, exv, trb):
if ext is not None: print "no not possible"
print "OK I caught you"
return True
with Mgr():
name='rubicon'/2 #to raise an exception
The return True
part is where the context manager decides to suppress the exception (as you do by not re-raising it in your except
clause).
The contextlib.contextmanager function decorator provides a handy way of providing a context manager without the need to write a full-fledged ContextManager
class of your own (with __enter__
and __exit__
methods, so you don't have to remember the arguments to the __exit__
method, or that the __exit__
method must return True
in order to suppress the exception). Instead, you write a function with a single yield
at the point you want the with
block to run, and you trap any exceptions (that effectively come from the yield
) as you normally would.
from contextlib import contextmanager
@contextmanager
def handler():
# Put here what would ordinarily go in the `__enter__` method
# In this case, there's nothing to do
try:
yield # You can return something if you want, that gets picked up in the 'as'
except Exception as e:
print "no not possible"
finally:
print "Ok I caught you"
with handler():
name='rubicon'/2 #to raise an exception
Why go to the extra trouble of writing a context manager? Code re-use. You can use the same context manager in multiple places, without having to duplicate the exception handling. If the exception handling is unique to that situation, then don't bother with a context manager. But if the same pattern crops up again and again (or if it might for your users, e.g., closing a file, unlocking a mutex), it's worth the extra trouble. It's also a neat pattern to use if the exception handling is a bit complicated, as it separates the exception handling from the main line of code flow.
The with
in Python is intended for wrapping a set of statements where you should set up and destroy or close resources. It is in a way similar to try...finally
in that regard as the finally clause will be executed even after an exception.
A context manager is an object that implements two methods: __enter__
and __exit__
. Those are called immediately before and after (respectively) the with
block.
For instance, take a look at the classic open()
example:
with open('temp.txt', 'w') as f:
f.write("Hi!")
Open returns a File
object that implements __enter__
more or less like return self
and __exit__
like self.close()
.