Context Manager

Conception

Context managers can be used for more than just opening and closing files.

If we think about it there are two phases to a context manager:

  1. when the with statement is executing: we enter the context

  2. when the with block is done: we exit the context

We can create our own context manager using a class that implements an __enter__ method which is executed when we enter the context, and an __exit__ method that is executed when we exit the context.

There is a general pattern that context managers can help us deal with:

  • Open - Close

  • Lock - Release

  • Change - Reset

  • Enter - Exit

  • Start - Stop

The __enter__ method is quite straightforward. It can (but does not have to) return one or more objects we then use inside the with block.

The __exit__ method however is slightly more complicated.

  1. It needs to return a boolean True/False. This indicates to Python whether to suppress any errors that occurred in the with block. As we saw with files, that was not the case - i.e. it returns a False

  2. If an error does occur in the with block, the error information is passed to the __exit__ method - so it needs three things: the exception type, the exception value and the traceback. If no error occured, then those values will simply be None.

Let's go ahead and create a context manager:

We can even cause an exception inside the with block:

We can change that by returning True from the __exit__ method:

Notice that the obj we obtained from the context manager, still exists in our scope after the with statement.The with statement does not have its own local scope - it's not a function! However, the context manager could manipulate the object returned by the context manager:

We still have access to res, but it's internal state was changed by the resource manager's __exit__ method.Although we already have a context manager for files built-in to Python, let's go ahead and write our own anyways - good practice.

Note that the __enter__ method can return anything, including the context manager itself.

If we wanted to, we could re-write our file context manager this way:

Working with Iterator

Additional Use

common patterns we can implement with context managers:

  • Open - Close

  • Change - Reset

  • Start - Stop

Decimal Contexts

If we create a decimal object, then it will use those settings.We can certainly change the properties of that global context:

Suppose now that we just want to temporarily change something in the context - we would have to do something like this:

Of course, this is kind of a pain to have to store the current value, set it to something new, and then remember to set it back to it's original value.

How about writing a context manager to do that seamlessly for us:

In fact, the decimal class already has a context manager, and it's way better than ours, because we can set not only the precision, but anything else we want:

Timing a with block

Decorator

Fortunately the standard library has already though of this - in fact that was one of the critical goals of Python's context managers - the ability to create context managers using generator functions (see PEP 343).

`Let's implement a timer.

Last updated

Was this helpful?