在Python 3.8的异步编程中,我如何使用'yield'语句?
在Python 3.8的异步编程中,我如何使用'yield'语句?
在Python的asyncio异步编程中(3.7版本或更低版本),如果我想手动让一个协程将控制权交还给主事件循环,我可以使用以下代码:\n
async def switch(): await asyncio.sleep(0) # 让出控制权给主事件循环 return async def task(): # ...做一些事情 # ... await switch() # 然后这个协程将被挂起,其他协程将被触发 # ... # ...再次被触发时做一些其他事情
\n然而,在Python3.8中,“@coroutine”装饰器已被弃用。此外,我不能在“async def”中使用yield(因为它将定义一个异步生成器而不是协程)。那么,我该如何实现相同的功能呢?
在Python 3.8的异步编程中,如何使用'yield'语句的问题是因为在某些情况下,我们需要显式地切换协程。然而,使用'yield'语句来切换协程并不是一个好的做法。相反,我们应该使用'asyncio.sleep(0)'来让其他协程运行。
几乎所有的事件循环都将其对应的持续时间为0的'sleep'方法解释为"让其他协程运行"。对于'asyncio',我们应该使用'asyncio.sleep(0)'来让其他协程运行。
下面是一个示例代码:
async def task(): # ...做一些事情 # ... await asyncio.sleep(0) # 这样协程将被挂起,其他协程将被触发 # ... # ...当再次触发时做一些其他的事情
以下是来自不同异步库的相关文档:
'asyncio'的文档中提到,'sleep()'总是会挂起当前任务,让其他任务运行。
'trio'的文档中提到,'await trio.sleep(0)'是一种执行检查点的惯用方式,而不做其他事情。
'curio'的文档中提到,如果睡眠的时间为0秒,则执行切换到下一个准备好的任务(如果有的话)。
如果出于某种原因需要显式地向事件循环发送'yield'信号,可以创建一个自定义类,并在其'__await__'特殊方法中使用'yield'。
以下是一个示例代码:
class Switch: """通过向事件循环发出yield信号来切换协程""" def __await__(self): yield
需要注意的是,这将向事件循环发送一个裸的'None'。事件循环如何处理该信号取决于使用的异步库。
还有一种方法是使用'.coroutine'装饰器来实现相同的功能。使用这种方法而不是'asyncio.sleep(0)'可以在我的电脑上加快约12%的速度。
以下是一些讨论中的代码片段:
# async def __await__(self): 代码中的'async'是否可以省略? 你是对的,谢谢你指出这个问题。现在已经修复了。
我们应该避免在Python 3.8的异步编程中使用显式的'yield'语句来切换协程。相反,我们应该使用'asyncio.sleep(0)'来让其他协程运行。如果确实需要显式地切换协程,可以使用自定义类并在其'__await__'特殊方法中使用'yield'。另外,使用'.coroutine'装饰器也可以实现相同的功能。