Не уверен, почему StopIteration не обрабатывается "yield from" - PullRequest
0 голосов
/ 09 декабря 2018

Глядя на следующий пример кода из книги «Свободный Python», который объясняет функциональность «двунаправленного туннеля» yield from, у меня возникает следующий вопрос:

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>


# 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("wrapped up grouper")
    print(results)


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],
}

Почему я получаю StopIteration исключение при замене вышеупомянутого делегирующего генератора следующим делегирующим генератором?

def grouper(results, key):
    results[key] = yield from averager()

Из того, что я узнал до сих пор, теоретически кажется, что удаление while True должно быть хорошо.group.send(None) вызовет сопрограмму averager() для break и вернет Result(...), который будет передан делегирующему генератору.И тогда делегирующий генератор завершит работу, присвоив этому Result(...) значение results[key].

Но вместо этого происходит следующее.

Traceback (mostrecent call last):
  File "coroaverager3.py", line 111, in <module>
    main(data)
  File "coroaverager3.py", line 83, in main
    group.send(None)  # important! <12>
StopIteration

Есть идеи?

1 Ответ

0 голосов
/ 09 декабря 2018

Вы правы, что делегирующий генератор присваивает results[key], но при этом он не завершает .Его выполнение продолжается, поскольку приостановить его негде.Конечно, он сразу падает с конца, в результате чего send(None) повышает StopIteration (поскольку для него нет значения (из yield) для его возврата).

while True: - это своего рода глупый способ добавить другое место для приостановки;один дополнительный yield после yield from будет немного более очевидным.(Он может предоставить значение, отличное от значения, предоставленного внутренним генератором, если клиент не всегда знает, когда завершится выполнение.)

...