#python #coroutine #yield-from
Вопрос:
Код приходит в форме свободного Python 1-е изд.,
Я не могу понять строку while True:
grouper
, удалить эту строку вызывает StopIteration
ошибку.
Но я нахожу новую версию grouper
without while True:
, которая работает. Зачем group.send(None)
нужен еще один цикл while True:
(или другой results[key] = yield from averager()
)?
Насколько я понимаю group.send(None)
, остановится yield from averager()
и присвоит results[key]
значение( Result(count, average)
). Это все.
from collections import namedtuple
Result = namedtuple('Result', 'count average')
# the subgenerator
def averager(): # <1>
total = 0.0
count = 0
average = None
while True:
term = yield # <2>
if term is None: # <3>
break
total = term
count = 1
average = total/count
return Result(count, average) # <4>
# the delegating generator
def grouper(results, key): # <5>
while True: # <6>
results[key] = yield from averager() # <7>
# Another version works
#def grouper(results, key):
# results[key] = yield from averager()
# results[key] = yield from averager()
# the client code, a.k.a. the caller
def main(data): # <8>
results = {}
for key, values in data.items():
group = grouper(results, key) # <9>
next(group) # <10>
for value in values:
group.send(value) # <11>
group.send(None) # important! <12>
# print(results) # uncomment to debug
report(results)
# output report
def report(results):
for key, result in sorted(results.items()):
group, unit = key.split(';')
print('{:2} {:5} averaging {:.2f}{}'.format(
result.count, group, result.average, unit))
data = {
'girls;kg':
[40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5],
'girls;m':
[1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43],
'boys;kg':
[39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3],
'boys;m':
[1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46],
}
if __name__ == '__main__':
main(data)
Ответ №1:
Это заставляет меня вспомнить, насколько хорош аскинио и почему все должны им пользоваться…
То, что происходит, лучше всего объяснить, пройдя через работу итераторов. Это внутренний генератор, упрощенный:
def averager():
local_var
while True:
term = yield
if term is None:
break
local_var = do_stuff(term)
return local_var
Это делает две вещи. Во-первых, он получает некоторые данные yield
(тьфу, объясняя, что выбор слов просто сбивает с толку) до тех пор, пока эти данные не None
будут . Затем, когда это так None
, он поднимает значение a StopIterationException
со значением local_var
. (Это то, что делает возврат от генератора).
Вот внешний генератор:
def grouper(results, key):
while True:
results[key] = yield from averager()
Что это делает, так это предоставляет выход внутреннего генератора вызывающему коду до тех пор , пока внутренний генератор не поднимется StopIterationException
, что автоматически фиксируется ( yield from
оператором) и назначается. Затем он готовится сделать то же самое снова.
Тогда у нас есть код вызова:
def main(data):
results = {}
for key, values in data.items():
group = grouper(results, key)
next(group)
for value in values:
group.send(value)
group.send(None)
Что это делает, так это:
- он повторяет внешний генератор ровно один раз
- это раскрывает производительность внутреннего генератора, и он использует это (
.send
) для связи с внутренним генератором. - он «завершает» внутренний генератор отправкой
None
, после чего заканчивается первыйyield from
оператор и присваивается переданное значение. - в этот момент внешний генератор готовится отправить другое значение
- цикл продолжается, и генератор удаляется с помощью сборки мусора.
что это за
while True:
петля?
Рассмотрим этот код, который также работает для внешнего генератора:
def grouper(result, key):
result[key] = yield from averager
yield 7
Единственное, что важно, — это то, что генератор не должен быть исчерпан, чтобы он не передавал исключение по цепочке, говорящее: «Мне больше нечего повторять».
P.S. смущен? Я был. Я должен был проверить это, прошло некоторое время с тех пор, как я пытался использовать coros на основе генератора. Они запланированы для удаления—используйте asyncio, это намного приятнее.
Комментарии:
1. Спасибо за подробное объяснение, я наконец понял, что
StopIteration
безwhile True:
исходит от истощенного внешнего генератора, поэтомуyield
для его подавления используется другой.