#python
#python
Вопрос:
Когда я создаю класс, который поддерживает with
область видимости, я получаю различное и необъяснимое поведение, когда я использую yield и return в качестве последних вызовов в __enter__
методе, code:
class TestWith:
def __init__(self, val):
self.val=val
def __enter__(self):
print("Entered!")
yield
# return
def __exit__(self, exc_type, exc_val, exc_tb):
print(f"In exit:, type: {exc_type}, val: {exc_val}, tb: {exc_tb}")
with TestWith(4):
print("printme")
Вывод:
printme
In exit:, type: None, val: None, tb: None
т. е. «Введено!» не печатается, изменяя __enter__
метод на:
def __enter__(self):
print("Entered!")
# yield
return
приводит к:
Entered!
printme
In exit:, type: None, val: None, tb: None
Почему существует различное поведение для этих двух реализаций? также я бы предположил, что «Введено!» будет напечатано независимо от того, что, поскольку оно вызывается перед return / yield
Комментарии:
1. Ну,
yield
иreturn
делают разные вещи, так почему вы думаете, что они оба должны работать здесь?2. @KonradRudolph они такие. Как они влияют на вызов функции, происходящий до их фактического вызова?
3. Потому что
return
иyield
не являются вызовами функций . Это инструкции потока управления, что означает, что они управляют тем, как ведет себя вся функция . В конкретном случаеyield
его использование приводит к тому, что функция имеет совершенно другую семантику, включая другое возвращаемое значение и другую последовательность инструкций.4. Это не связано с контекстными менеджерами, которые только усложняют пример. Вопрос в основном заключается в том, «что такое функция генератора».
Ответ №1:
Использование yield
в определении функции превращает функцию в генератор и работает она совершенно по-другому. Вот пример:
&&t;&&t;&&t; def func1():
... print('hi')
... yield
...
&&t;&&t;&&t; def func2():
... print('hi')
... return
...
&&t;&&t;&&t; func1() # returns a &enerator...doesn't execute the function!
<&enerator object func1 at 0x000002555B088510&&t;
&&t;&&t;&&t; func2()
hi
&&t;&&t;&&t; &=func1() # save the &enerator
&&t;&&t;&&t; next(&) # execute it to the next yield
hi
&&t;&&t;&&t; next(&) # No more yields throws an exception
Traceback (most recent call last):
File "<stdin&&t;", line 1, in <module&&t;
StopIteration
__enter__
предполагается, что это обычная функция, а не генератор, поэтому не используйте yield
в этом случае.
Ответ №2:
Возможно, вы путаете «синтаксис yield» для создания контекстного менеджера (добавление with
поддержки функции без необходимости создания класса).
from contextlib import contextmana&er
@contextmana&er
def TestWith(val):
print("Entered!")
yield
print("Exit")
with TestWith(4):
print("printme")
Вывод:
Entered!
printme
Exit
Комментарии:
1. Действительно, использование context mana&er привело меня к этому странному сценарию, но на самом деле это не дает ответа на данный вопрос. Спасибо за пример