Страницы

Поиск по вопросам

среда, 25 декабря 2019 г.

Вопрос по x = yield f() — какой порядок вызова и присваивания?

#python #python_3x


объясните, что означает эта строка внутри функции

some_variable = yield some_function()


если я правильно понимаю, то yield some_function() в данном случае отправляет результат
в генератор, после чего записывается в переменную и продолжается выполнение кода после
этого присваивания.

Если я понял верно, тогда вопрос. А присваивание происходит после того, как происходит
возврат и обработка текущего результата, или сначала выполняется до конца текущая функция,
затем в генератор возвращается результат?
    


Ответы

Ответ 1



Если создать генератор 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 "", line 1, in StopIteration Подобные генераторы или как их иногда называют сопрограммы (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.

Ответ 2



Вот примерное объяснение: some_function = lambda: 'some_function result' def func(): while True: value = some_function() print('value', value) result = yield value print('result', result) g = func() value = next(g) # инициализация генератора(обязательно), и получение value value = next(g) # и еще просто получить value g.send(1) # передаем 1 в генератор, теперь result = 1 value = g.send(2) # передаем 2 в генератор, теперь result = 2. Также получаем value То есть если вы хотите просто получить value то нужно просто value = next(g). Если хотите установить result в YOU_RESULT и получить value то нужно value = g.send(YOU_RESULT). Еще учтите, что для g.send(YOU_RESULT) необходима первичная инициализация g - next(g). Это все дело необходимо для https://ru.wikipedia.org/wiki/Сопрограмма Вот собственно и все. Задавайте вопросы если непонятно

Комментариев нет:

Отправить комментарий