Python 3: метод отправки генераторов
Я не могу понять метод send
. Я понимаю, что он используется для работы генератора. Но
синтаксис здесь: generator.send(value)
.
Я почему-то не могу понять, почему значение должно стать результатом текущего выражения yield
. Я подготовил пример:
def gen():
for i in range(10):
X = yield i
if X == 'stop':
break
print("Inside the function " + str(X))
m = gen()
print("1 Outside the function " + str(next(m)) + '\n')
print("2 Outside the function " + str(next(m)) + '\n')
print("3 Outside the function " + str(next(m)) + '\n')
print("4 Outside the function " + str(next(m)) + '\n')
print('\n')
print("Outside the function " + str(m.send(None)) + '\n') # Start generator
print("Outside the function " + str(m.send(77)) + '\n')
print("Outside the function " + str(m.send(88)) + '\n')
#print("Outside the function " + str(m.send('stop')) + '\n')
print("Outside the function " + str(m.send(99)) + '\n')
print("Outside the function " + str(m.send(None)) + '\n')
Результат:
1 Outside the function 0
Inside the function None
2 Outside the function 1
Inside the function None
3 Outside the function 2
Inside the function None
4 Outside the function 3
Inside the function None
Outside the function 4
Inside the function 77
Outside the function 5
Inside the function 88
Outside the function 6
Inside the function 99
Outside the function 7
Inside the function None
Outside the function 8
Ну, честно говоря, меня это удивило.
- В документации мы можем прочитать, что при выполнении оператора
yield
состояние генератора замораживается, а значение expression_list
возвращается вызывающему абоненту next
.
Ну, похоже, это не случилось. Почему мы можем выполнить инструкцию if
и print
внутри gen()
.
- Как я могу понять, почему
X
внутри и снаружи функции отличается?
ОК. Предположим, что send(77)
передает 77 в m
. Ну, выражение yield
становится 77.
Тогда что такое X = yield i
? И как 77 внутри функции преобразуется в 5, когда происходит снаружи?
- Почему первая строка результата не отражает ничего, что происходит внутри генератора?
В любом случае, не могли бы вы как-нибудь прокомментировать эти инструкции send
и yield
?
Ответы
Ответ 1
Когда вы используете send
и выражение yield
в генераторе, вы рассматриваете его как сопрограмму; отдельный поток выполнения, который может запускаться последовательно чередующимся, но не параллельно его вызывающему.
Когда вызывающий выполняет R = m.send(a)
, он помещает объект a
во входной слот генератора, передает управление генератору и ожидает ответа. Генератор получает объект a
в результате X = yield i
и работает до тех пор, пока он не достигнет другого выражения yield
, например. Y = yield j
. Затем он помещает j
в свой выходной слот, передает управление обратно вызывающему абоненту и ждет, пока он снова не возобновится. Вызывающий получает j
в результате R = m.send(a)
и запускается до тех пор, пока не достигнет другого оператора S = m.send(b)
и т.д.
R = next(m)
является таким же, как R = m.send(None)
; он помещает None
во входной слот генератора, поэтому, если генератор проверяет результат X = yield i
, тогда X
будет None
.
В качестве метафоры рассмотрите тупой официант:
![Dumb waiter]()
Когда сервер получает заказ от клиента, они помещают блокнот в немого официанта, send
его на кухню и ждут у люка для блюда:
R = kitchen.send("Ham omelette, side salad")
Шеф-повар (который ждал у люка) берет заказ, готовит блюдо, yield
в ресторан и ждет следующего заказа:
next_order = yield [HamOmelette(), SideSalad()]
Сервер (который ждал у люка) берет блюдо у клиента и возвращается с другим заказом и т.д.
Поскольку и сервер, и шеф-повар ждут у люка после send
заказа или yield
с блюдом, только один человек делает что-либо в любой момент, то есть процесс является однопоточным. Обе стороны могут использовать нормальный поток управления, поскольку генераторная техника (тупой официант) заботится о выполнении чередования.
Ответ 2
Самая запутанная часть должна быть этой строкой X = yield i
, особенно когда вы вызываете send()
на генераторе. На самом деле вам нужно знать только следующее:
на лексическом уровне:
next()
равно send(None)
на уровне интерпретатора:
X = yield i
равно ниже строк (ЗАПИСЬ ВОПРОСОВ):
yield i
# won't continue until next() or send() is called
# and this is also the entry point of next() or send()
X = the_input_of_send
и две строки комментария являются точной причиной, поэтому нам нужно сначала вызвать send(None)
, потому что генератор вернет i
(yield i
) до присвойте значение X
Ответ 3
def gen():
i = 1
while True:
i += 1
x = yield i
print(x)
m = gen()
next(m)
next(m)
m.send(4)
результат
None
4
просмотрите более упрощенные коды выше.
Я думаю, что это привело к вашей путанице: "x = yield i",
этот статус не говорит значение, принятое из метода send(), связанного с i, тогда я привязан к x.
Вместо этого значение я возвращается с помощью атрибута yield в генератор, x определяется методом send(). Один оператор выполняет две операции одновременно.