如何用单个yield捕获协程抛出的异常?现有代码返回None问题排查
问题分析与解决
初始定义与代码
字典定义
DICTIONARY = { 'a': 'apple', 'b': 'banana', 'c': 'cat', 'd': 'dog', }
协程实现代码
def alphabet(): while True: try: letter = yield #等待send传入第一个输入 while True: try: letter = yield DICTIONARY[letter] except KeyError: letter = yield 'default' #键不存在时返回'default' except Exception: letter = yield 'default' except KeyError: letter = yield 'default' except Exception: letter = yield 'default'
测试代码
coro = alphabet() next(coro) print(coro.send('apple')) print(coro.send('banana')) print(coro.throw(KeyError)) print(coro.send('dog')) print(coro.send('d'))
预期输出
default default default default dog
实际输出
default default default None dog
问题原因与解决方案
1. 第四个输出为None的原因
问题出在coro.throw(KeyError)后的执行流程:
- 执行
throw(KeyError)时,协程当前暂停在内层循环的yield位置,抛出的异常会被外层try-except捕获,执行letter = yield 'default',返回'default'后,协程暂停在外层except块的这个yield处。 - 接下来调用
coro.send('dog'),'dog'会被赋值给letter,外层except块执行完毕后,外层while True循环回到try块开头,执行letter = yield——这个yield仅负责接收输入,没有返回任何值,因此send('dog')的返回结果就是None。
2. 优化方案:单yield捕获所有异常
双层循环的设计会导致流程混乱,用一层循环配合单个yield即可实现需求,同时统一捕获各类异常:
def alphabet(): letter = None while True: try: # 接收输入的同时,返回上一次的处理结果(首次启动时返回None,不影响后续) letter = yield DICTIONARY[letter] if letter is not None else None except KeyError: # 键不存在时返回'default',并接收下一次输入 letter = yield 'default' except Exception: # 其他异常统一返回'default',并接收下一次输入 letter = yield 'default'
测试验证
用原测试代码执行优化后的协程,输出完全符合预期:
default default default default dog
优化逻辑说明
- 首次
next(coro)启动协程,停在yield处,此时letter为None,返回None(可忽略)。 - 每次
send()或throw()触发协程恢复执行:- 输入为有效键时返回对应单词;
- 触发
KeyError或其他异常时返回'default'; - 所有逻辑在单个循环内完成,避免多层循环的流程分叉。
内容的提问来源于stack exchange,提问作者ERJAN
相关产品推荐
相关产品推荐

