Python Glossary English

все Python 3ООПпонятие

Протокол контекстного менеджера

контекст. менеджер

Контекстный менеджер (with-statement). __enter__ выполняется при входе, __exit__ — при выходе (в том числе при исключении).

Синтаксис

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

__exit__ получает информацию об исключении в трёх аргументах, и если вернуть из него True — исключение будет подавлено (при обычном выходе там None). Возвращать True по привычке не стоит: молча проглоченная ошибка — классический источник трудноуловимых багов. Значение, которое вернул __enter__, попадает в переменную после as.

Примеры

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

См. также

Открыть в полном глоссарииdocs.python.orgСообщить о неточности