在 python3 yield from 中,抛出 StopIteration 异常的条件是什么?

个人理解:
调用方 .send(None) 后,在 子生成器 break 时抛出 StopIteration 异常,return 返回值后在委托生成器中赋值给 yield from 表达式左侧变量并捕捉 子生成器 抛出的 StopIteration 异常。

问题:
但是为什么此时如果 委托生成器 没有 while True: ,又会抛出 StopIteration 异常?
原代码:

# 子生成器
def average_gen():
    total = 0
    count = 0
    average = 0
    while True:
        new_num = yield average
        if new_num is None:
            break
        count += 1
        total += new_num
        average = total/count

    # 每一次return,都意味着当前协程结束。
    return total,count,average

# 委托生成器
def proxy_gen():
    while True:
        # 只有子生成器要结束(return)了,yield from左边的变量才会被赋值,后面的代码才会执行。
        total, count, average = yield from average_gen()
        print("计算完毕!!\n总共传入 {} 个数值, 总和:{},平均数:{}".format(count, total, average))

# 调用方
def main():
    calc_average = proxy_gen()
    next(calc_average)            # 预激协程
    print(calc_average.send(10))  # 打印:10.0
    print(calc_average.send(20))  # 打印:15.0
    print(calc_average.send(30))  # 打印:20.0
    calc_average.send(None)      # 结束协程
    # 如果此处再调用calc_average.send(10),由于上一协程已经结束,将重开一协程

if __name__ == '__main__':
    main()
    

委托生成器 中 while True: 的作用是什么呢?
为什么去掉 委托生成器 的 while True: 会抛出 StopIteration 异常呢?
或者说,为什么在 委托生成器 中加上 while True: 就不会抛出 StopIteration 异常了呢?
修改后的代码:

# 子生成器
def average_gen():
    total = 0
    count = 0
    average = 0
    while True:
        new_num = yield average
        if new_num is None:
            break
        count += 1
        total += new_num
        average = total/count

    # 每一次return,都意味着当前协程结束。
    return total,count,average

# 委托生成器
def proxy_gen():
    # while True:
        # 只有子生成器要结束(return)了,yield from左边的变量才会被赋值,后面的代码才会执行。
        total, count, average = yield from average_gen()
        print("计算完毕!!\n总共传入 {} 个数值, 总和:{},平均数:{}".format(count, total, average))

# 调用方
def main():
    calc_average = proxy_gen()
    next(calc_average)            # 预激协程
    print(calc_average.send(10))  # 打印:10.0
    print(calc_average.send(20))  # 打印:15.0
    print(calc_average.send(30))  # 打印:20.0
    calc_average.send(None)      # 结束协程
    # 如果此处再调用calc_average.send(10),由于上一协程已经结束,将重开一协程

if __name__ == '__main__':
    main()

异常信息:

10.0
15.0
20.0
计算完毕!!
总共传入 3 个数值, 总和:60,平均数:20.0
Traceback (most recent call last):
  File "f:\VSCodeSC\python\tempCodeRunnerFile.python", line 35, in <module>
    main()
  File "f:\VSCodeSC\python\tempCodeRunnerFile.python", line 31, in main
    calc_average.send(None)      # 结束协程
StopIteration
阅读 4.6k
2 个回答

生成器在return或函数体结束处触发StopIteration异常(和while break无关),如果有return值,作为StopIteration的Value

而 yield from generator 最终会将捕捉到的generator的StopIteration异常的value传递到左方,在这层上,generator的return value 看上去又像是函数返回值了。

同样,外部委托代理也是在return或函数体结束处触发StopIteration异常, 如果有return值,作为StopIteration的Value

我的理解是 委托生成器 没有 while True时,
StopIteration异常是委托生成器退出时,抛出的异常, 而不是子生成器抛出的异常,
相反子生成器抛出的StopIteration异常在委托生成器 yield from 中处理了
你可以尝试捕获这个异常, 这个异常对象的value属性是None

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题