Подтвердить что ты не робот

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?

4b9b3361

Ответ 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(). Один оператор выполняет две операции одновременно.