ruk·si

🐍 Python
Context Managers

Updated at 2018-06-11 00:57

Context managers are simplified but powerful try/finally pattern. Context managers need to implement __enter__ and __exit__. Exit method will be performed after any exceptions, return or even sys.exit(). You use context managers by using with keyword.

class Greeting:

    def __init__(self, text: str) -> None:
        self.text: str = text

    def __enter__(self) -> str:
        return self.text

    def __exit__(self, exc_type, exc_val, exc_tb) -> None:
        print('exit')

with Greeting('hello') as g:
    assert g == 'hello'

# => exit

contextlib has a helper decorator for turning a generator to a context manager.

from contextlib import contextmanager, closing

@contextmanager
def greeting(text: str) -> str:
    try:
        yield text
    finally:
        print('exit')

with greeting('hello') as g:
    assert g == 'hello'

# => exit

# also, if we would yield a connection, a file or something else closeable,
# you can do the following so closing is done automatically
# when value goes out of scope

# with closing(connection):
#     yield connection

The same context manager instance can be used multiple times to pass on state.

class Indenter:

    def __init__(self, amount: int) -> None:
        self.level = 0
        self.amount = amount

    def __enter__(self) -> 'Indenter':
        self.level += 1
        return self

    def __exit__(self, exc_type, exc_val, exc_tb) -> None:
        self.level -= 1

    def echo(self, text: str) -> None:
        print(' ' * (self.level * self.amount) + text)

with Indenter(4) as indent:
    indent.echo('hi!')
    with indent:
        indent.echo('hello')
        with indent:
            indent.echo('bonjour')
    indent.echo('hey')

# =>     hi!
# =>         hello
# =>             bonjour
# =>     hey

Source

  • Python Tricks The Book, Dan Bader
  • Fluent Python, Luciano Ramalho