Python Glossary Русский

all Python 3OOPterm

Context manager protocol

context manager

A context manager (the with statement). __enter__ runs on entry, __exit__ on exit — including when an exception is raised.

Syntax

class CM:
    def __enter__(self): return self
    def __exit__(self, exc_type, exc_val, tb): ...

__exit__ receives the exception details in its three arguments, and returning True from it suppresses that exception (on a normal exit they are all None). Don't return True out of habit — a silently swallowed error is a classic source of hard-to-find bugs. Whatever __enter__ returns is what the as variable binds to.

Examples

class ManagedResource:
    def __enter__(self):
        print('Opening resource')
        return self
    def __exit__(self, exc_type, exc_val, tb):
        print('Closing resource')
        return False  # не подавлять исключения
with ManagedResource() as r:
    print('Using resource')  # → Opening / Using / Closing
class Timer:
    import time as _time
    def __enter__(self):
        import time
        self.start = time.time()
        return self
    def __exit__(self, *args):
        import time
        self.elapsed = time.time() - self.start
        print(f'Elapsed: {self.elapsed:.4f}s')
with Timer() as t:
    sum(range(10**6))  # → Elapsed: ...
class Transaction:
    def __init__(self):
        self.operations = []
    def __enter__(self):
        return self
    def __exit__(self, exc_type, exc_val, tb):
        if exc_type:
            print('Rolling back!')
            self.operations.clear()
        else:
            print('Committing:', self.operations)
        return True  # подавить исключение
with Transaction() as tx:
    tx.operations.append('insert')
    tx.operations.append('update')  # → Committing: ['insert','update']
# Подавление исключения (__exit__ возвращает True)
class Suppress:
    def __init__(self, *exceptions):
        self.exceptions = exceptions
    def __enter__(self):
        return self
    def __exit__(self, exc_type, exc_val, tb):
        return exc_type is not None and issubclass(exc_type, self.exceptions)
with Suppress(ZeroDivisionError):
    x = 1/0  # → не падает!
print('After suppress')  # → After suppress
# Использование contextlib
from contextlib import contextmanager
@contextmanager
def managed():
    print('enter')
    yield
    print('exit')
with managed():
    print('body')  # → enter / body / exit
# Вложенные with
class A:
    def __enter__(self): print('A enter'); return self
    def __exit__(self, *a): print('A exit'); return False
class B:
    def __enter__(self): print('B enter'); return self
    def __exit__(self, *a): print('B exit'); return False
with A() as a, B() as b:
    print('inside')  # → A enter / B enter / inside / B exit / A exit

See also

Open in the full glossarydocs.python.orgReport an inaccuracy