Зачем выводить StopItation из цикла while - PullRequest
1 голос
/ 22 июня 2019

Я использую yield from, но я не знаю о влиянии while на yield.Если я помещаю yield from в while цикл, он работает хорошо, но когда я в среднем отменяю цикл, возникает исключение.

final_result = {}
def sales_sum(pro_name):
    total = 0
    nums = []
    while True:
        x = yield
        print(pro_name+" Sales volume: ", x)
        if not x:
            break
        total += x
        nums.append(x)
    return total, nums

def middle(key):
    while True:
        final_result[key] = yield from sales_sum(key)

def middle2(key):
    final_result[key] = yield from sales_sum(key)

def main(fun):
    data_sets = { "A": [1200, 1500], "B": [28,55,98]}
    for key, data_set in data_sets.items():
        m = fun(key)
        m.send(None)
        for value in data_set:
            m.send(value)
        m.send(None)

if __name__ == '__main__':
    main(middle) # work well
    main(middle2) # StopIteration

Я ожидаю, что main(middle2) будет работать как main(middle), но есть исключение StopIteration.

Ответы [ 2 ]

1 голос
/ 22 июня 2019

Причиной неожиданного исключения StopIteration в main является то, что ваш вызов m.send(None) приводит к исчерпанию вашего генератора middle2 (после того, как вспомогательный генератор sales_sum выходит из цикла в ответ наценность фальси это получила).Когда генератор исчерпан, он поднимает StopIteration.Обычно это невидимо, потому что вы используете итераторы в циклах for, но в этом случае это нарушает ваш код.

Существует несколько способов исправить это.Можно было бы использовать вызов с двумя аргументами next вместо использования m.send(None):

next(m, None)

Это делает то же самое, что и m.send(None), но имеет дополнительное преимущество подавления StopIteration.Обратите внимание, что None в вызове next на самом деле не совпадает с * в 1019 *.Это возвращаемое значение по умолчанию в случае исчерпанного итератора, а не значение, которое отправляется (которое всегда None при использовании next).

Другим подходом было бы изменить middle2 такчто это не заканчивается, когда генератор sales_sum делает.Вы можете добавить дополнительный оператор yield в конце, чтобы он возвращал управление в последний раз после выполнения присваивания для final_result, когда его суб-генератор возвращает.

Последняя идея - заменить m.send(None) с m.close().Это потребует некоторых изменений в final_result, так как вызов close вызовет исключение GeneratorExit в генераторе.Если вы ожидаете этого, вы можете использовать его в качестве сигнала для выполнения вместо поиска фальшивого значения:

def sales_sum(pro_name):
    total = 0
    nums = []
    while True:
        try:
            x = yield
        except GeneratorExit:
            return total, nums

        print(pro_name+" Sales volume: ", x)
        total += x
        nums.append(x)

С этим изменением middle2 не будет нуждаться в каких-либо изменениях.

1 голос
/ 22 июня 2019

sales_sum - конечный итератор.middle2 перебирает его ровно один раз;middle пытается выполнить итерацию несколько раз.

...