为何内部协程中GeneratorExit实例被替换?如何传递参数?
异步库开发中
coroutine.throw()的GeneratorExit参数传递问题解决 核心问题
- 直接调用
async_fn()时,Exception、BaseException、GeneratorExit三类异常的实例ID和捕获的完全一致,async_fn()能正常接收'Hello'参数,测试全过。 - 用
wrapper()异步函数包裹async_fn()后,抛出的GeneratorExit实例ID和捕获的不一样,导致没法识别'Hello'参数,测试失败。 - 怀疑
GeneratorExit不能直接实例化,得通过coroutine.close()创建,但不知道怎么传递参数。
可行解决方法
1. 别依赖实例ID,用自定义异常子类传参
直接定义一个继承自GeneratorExit的子类,把参数存在自定义属性里,这样不管实例ID变不变,只要捕获到这个子类就能拿到参数:
class CustomGeneratorExit(GeneratorExit): def __init__(self, msg): self.msg = msg # 抛出时用自定义子类 coro.throw(CustomGeneratorExit('Hello')) # 在async_fn里捕获 async def async_fn(): try: await asyncio.sleep(1) except CustomGeneratorExit as e: print(e.msg) # 这里就能拿到'Hello'
2. 给协程对象绑定自定义属性传参
coroutine.close()自动抛出的GeneratorExit没法直接带参数,但可以提前给协程对象绑个属性,捕获异常时读取这个属性:
async def wrapper(): coro = async_fn() # 给协程绑自定义参数 coro._custom_msg = 'Hello' await coro async def async_fn(): try: await asyncio.sleep(1) except GeneratorExit: # 读取绑定的参数 msg = getattr(self, '_custom_msg', None) print(msg) # 拿到'Hello'
3. 在wrapper里转发异常时保留参数
如果必须用原生GeneratorExit,可以在wrapper里捕获异常后,重新构造异常并把参数存进去,再转发给内部协程:
async def wrapper(): coro = async_fn() try: await coro except GeneratorExit as e: # 保留原参数或手动传入自定义参数 new_exc = GeneratorExit() new_exc.msg = getattr(e, 'msg', 'Hello') coro.throw(new_exc) # async_fn里的捕获逻辑 async def async_fn(): try: await asyncio.sleep(1) except GeneratorExit as e: print(e.msg) # 拿到参数
关键提醒
GeneratorExit是CPython内部特殊处理的异常,多层协程包裹时,解释器可能会重新生成实例,导致ID变化,所以绝对不能靠实例ID来判断异常。上面几种方法都绕开了实例ID的依赖,不管实例怎么变都能正常传递参数。
内容的提问来源于stack exchange,提问作者Nattōsai Mitō
相关产品推荐
相关产品推荐

