#python #generator #contextmanager
#python #генератор #contextmanager
Вопрос:
from contextlib import contextmanager
@contextmanager
def context():
print "entering"
yield
print "exiting"
def test():
with context():
for x in range(10):
yield x
for x in test():
if x == 5:
break # or raise
вывод:
entering
Есть ли способ заставить python автоматически вызывать __exit__
метод context() при прерывании for
цикла? Или какой-то другой способ достижения той же цели? То, что я знаю о генераторах и контекстных менеджерах, заставляет меня подозревать, что это невозможно, но это делает контекстные менеджеры довольно бесполезными внутри генераторов, не так ли? Мне кажется, yield
инструкция внутри with
блока должна вызывать красный флаг, context manager __exit__
может не запускаться.
Ответ №1:
Ну, вы могли бы обернуть функцию yield в context() предложением try / finally:
from contextlib import contextmanager
@contextmanager
def context():
print "entering"
try:
yield
finally:
print "exiting"
def test():
with context():
for x in range(10):
yield x
for x in test():
if x == 5:
break # or raise
вывод:
entering
exiting
Редактировать: Если вы попробуете: help(contextmanager), он покажет, что это «типичный» пример использования, где они завершают yield предложением try / finally .
Комментарии:
1. Это именно то, что вы должны делать: после того, как цикл
test()
будет удален, который возникаетGeneratorExit()
приyield x
. Это, в свою очередь, выходит изwith: for:
блока. Затем contextmanagerGeneratorExit()
снова перехватывает и выводит его с выходом вcontext
— и вот где вы должны его перехватить, иначеcontext
все заканчивается прямо там (в конце концов, это тоже генератор, поэтому это исключение завершает его без выполнения очистки). В документах указано, что: «Таким образом, вы можете использовать оператор try … except … finally для исправления ошибки (если таковая имеется) или обеспечения выполнения некоторой очистки»