Уроки модуля (3/4)
Менеджер контекста: с помощью и __enter__/__exit__
Контекстный менеджер — это объект, который вы используете с with. Это гарантирует, что
определенные операции установки/очистки выполняются всегда, даже при возникновении ошибок.
Канонический пример: открытие и закрытие файла.
Шаблон with
with open("note.txt", "w") as f:
f.write("ciao")
# qui f è già chiuso, anche se write() ha sollevato un'eccezioneНа простом английском языке: «с ресурсом X, открытым как f, сделайте это. Когда закончите
(что бы ни случилось), закройте X». Без with вам пришлось бы писать
try/finally каждый раз.
Более одного за раз
with open("a.txt") as f, open("b.txt") as g:
dati_a = f.read()
dati_b = g.read()Написание собственного контекстного менеджера (класса)
Вам необходимо определить два метода dunder: __enter__ и __exit__.
class Timer:
def __enter__(self):
import time
self.t0 = time.perf_counter()
return self # è l'oggetto che finisce dopo "as"
def __exit__(self, exc_type, exc_value, traceback):
import time
self.elapsed = time.perf_counter() - self.t0
# ritornare True ingoia l'eccezione; di solito non lo vuoi
return False
with Timer() as t:
sum(range(10**6))
t.elapsed # quanto è durato__enter__(self)вызывается при входе в блок; возвращаемое значение — это то, что вы получаете послеas.__exit__(self, exc_type, exc_value, tb)вызывается при выходе (всегда). Если возвращаетсяTrue, любые исключения подавляются.
Ярлык: @contextmanager
contextlib.contextmanager позволяет написать менеджер контекста в виде
функция генератора:
from contextlib import contextmanager
@contextmanager
def timer():
import time
t0 = time.perf_counter()
try:
yield # qui sta il blocco "with"
finally:
elapsed = time.perf_counter() - t0
print(f"durata: {elapsed:.4f}s")
with timer():
sum(range(10**6))Код до yield — __enter__, код после — __exit__.
try/finally гарантирует очистку даже при возникновении ошибок.
подавить: игнорировать исключение
from contextlib import suppress
with suppress(FileNotFoundError):
open("forse-non-esiste.txt").read()
# se il file non c'è, nessun errore propagaОшибка: with только для объектов, которые его поддерживают.
x = 42
with x: # AttributeError: __enter__
...Написание контекстных менеджеров с помощью @contextmanager
Модуль contextlib стандартной библиотеки экспортирует очень удобный декоратор @contextmanager для создания менеджеров контекста с использованием функций генератора вместо написания многословных классов, реализующих методы __enter__ и __exit__. Вы просто пишете функцию-генератор, которая использует один yield:
from contextlib import contextmanager
@contextmanager
def manage_resource():
# __enter__ code
yield resource
# __exit__ codeПопробуйте сами
Используйте contextlib.suppress, чтобы попробовать int('non-numero') игнорировать ValueError. После блока назначьте `ok = True`. Оцените «ОК».
Показать подсказку
подавление (ValueError) поглощает только ValueError.
Решение доступно после 3 попыток
Обзор упражнения
Напишите менеджер контекста «collect» с помощью @contextmanager, который выдает пустой список и при выходе преобразует его в кортеж (распечатайте его). Используйте его, чтобы добавить 1, 2, 3 в список внутри блока. В конце оцените `len(collection)`, где `collection` — это полученный список.
Показать подсказку
выведите элементы, чтобы предоставить список внутри блока with.
Решение доступно после 3 попыток
Дополнительная задача
Write a custom context manager using the `@contextmanager` decorator from `contextlib`. The context manager must be named `mock_session`. Upon entry, it must append `'start'` to a global list `log_list = []`, yield control using `yield`, and upon exit append `'stop'`. Finally, evaluate `log_list` after using the context manager with `with mock_session():`.
Показать подсказку
Используйте выход внутри Mock_session, чтобы приостановить выполнение перед добавлением «stop».
Решение доступно после 3 попыток