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