объясните, что означает эта строка внутри функции
some_variable = yield some_function()
если я правильно понимаю, то yield some_function() в данном случае отправляет результат в генератор, после чего записывается в переменную и продолжается выполнение кода после этого присваивания.
Если я понял верно, тогда вопрос. А присваивание происходит после того, как происходит возврат и обработка текущего результата, или сначала выполняется до конца текущая функция, затем в генератор возвращается результат?
Ответ
Если создать генератор g = gen(), то сам он ничего не делает, пока снаружи его не продвинут с помощью next(g). next(g) равнозначен g.send(None)
g.send(y) позволяет передать в генератор значение:
def gen():
while True:
x = yield '.'
print(x * x)
Так как g = gen() само по себе не продвигает генератор, то необходимо вызывать next(g), чтобы код дошёл до строчки с x = yield '.'. Так как в начале функции нет yield, то первый вызов всегда эквивалентен g.send(None). Значения y, переданные в последующих вызовах, уже присваиваются x
>>> g = gen()
>>> z = g.send(None)
>>> z
'.'
>>> z = g.send(1)
1
>>> z
'.'
>>> z = g.send(2)
4
>>> z
'.'
В данном случае генератор генерирует '.' постоянно.
Видно, что первый вызов g.send(None) не дошёл до x (иначе в дальнейшем получили бы TypeError из-за None * None). То есть значение переданное в g.send() возвращается из yield, на котором генератор стоял, а затем код продвигается до следующего yield и его аргумент возвращается как результат и присваивается z
Теперь можно объяснить, что происходит с:
def gen():
some_variable = yield some_function()
по шагам:
Создаём геренратор:
>>> g = gen()
Код внутри генератора при этом не выполняется
Продвигаем код до первого yield, вызывается some_function(), получаем значение some_function()
>>> g.send(None)
'result from some_function()'
Имя some_variable ещё не присвоено по окончанию g.send()
Следующий вызов g.send(1) присваивает 1 к some_variable имени и выбрасывает StopIteration, когда код покидает генератор:
>>> g.send(1)
Traceback (most recent call last):
File "
Подобные генераторы или как их иногда называют сопрограммы (coroutine) могут использоваться, чтобы перемежать исполнение нескольких функций в одном потоке. Практический пример, можно выполнить NREQUESTS сетевых запросов по NCONCURRENT одновременных запросов, используя twisted.internet.defer.inlineCallbacks декоратор:
#!/usr/bin/env python
"""Make NREQUESTS http POST requests `NCONCURRENT` at the time using treq module."""
import os
from twisted.internet import defer, task # $ pip install twisted
import treq # $ pip install treq
def n_at_a_time(it, n):
"""Iterate over `it` concurently `n` items at a time.
`it` - an iterator creating Deferreds
`n` - number of concurrent iterations
return a deferred that fires on completion
"""
return defer.DeferredList([task.coiterate(it) for _ in range(n)])
@defer.inlineCallbacks
def make_request():
# post request
response = yield treq.post('http://localhost:8001/post', {'a':'b', 'c': 'd'})
data = yield response.content() # read response
def main(reactor):
reqs = (make_request() for _ in range(int(os.environ['NREQUESTS'])))
return n_at_a_time(reqs, n=int(os.environ['NCONCURRENT']))
task.react(main)
По похожему, но несколько другому механизму (основанному на yield from, а не yield) работают asyncio coroutines.
Комментариев нет:
Отправить комментарий