What is the purpose of a context manager in python [duplicate]

What are context managers?

Context managers are there to aid with resources (although may be used for much more). The context is basically the resource.

Let's say you opened a file for writing:

f = open(path, "w")

You now have an open file handle. During the handling of your file, no other program can write to it. In order to let other programs write to it, you must close the file handle:

f.close()

But, before closing your file an error occured:

f = open(path, "w")
data = 3/0  # Tried dividing by zero. Raised ZeroDivisionError
f.write(data)
f.close()

What will happen now is that the function or entire program will exit, while leaving your file with an open handle. (CPython cleans handles on termination and handles are freed together with a program but you shouldn't count on that)

A with statement ensures that as soon as you leave it's indentation, it will close the file handle:

with open(path, "w") as f:
    data = 3/0  # Tried dividing by zero. Raised ZeroDivisionError
    f.write(data)
# Here, the file is already closed automatically, no matter what happened.

with statements may be used for many more things. For example: threading.Lock()

lock = threading.Lock()
with lock:  # Lock is acquired
   do stuff...
# Lock is automatically released.

Almost everything done with a context manager can be done with try: ... finally: ... but context managers are nicer to use, more comfortable, more readable and by implementing __enter__ and __exit__ provide an easy to use interface.


How are context managers implemented?

or What are __enter__ and __exit__?

Creating context managers is done by implementing __enter__() and __exit__() in a normal class.

__enter__() tells what to do when a context manager starts and __exit__() when a context manager exists (giving the exception to the __exit__() method if an exception occurred)

A shortcut for creating context managers can be found in contextlib. It wraps a generator as a context manager like so:

from contextlib import contextmanager

@contextmanager
def tag(name):
    print("<%s>" % name)
    yield
    print("</%s>" % name)

>>> with tag("h1"):
...    print("foo")
...
<h1>
foo
</h1>

Example taken from the Python docs.