Модульні уроки (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`. Оцініть `ок`.
Показати підказку
suppress(ValueError) ковтає лише ValueError.
Рішення доступне після 3 спроб
Повторна вправа
Напишіть контекстний менеджер `collect` за допомогою @contextmanager, який дає порожній список і після виходу перетворює його на кортеж (надрукуйте його). Використовуйте його, щоб додати 1, 2, 3 до списку всередині блоку. Наприкінці обчисліть `len(collection)`, де `collection` — це отриманий список.
Показати підказку
yield елементи, щоб відкрити список усередині блоку 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():`.
Показати підказку
Використовуйте yield всередині mock_session, щоб призупинити виконання перед додаванням 'stop'.
Рішення доступне після 3 спроб