在Python 3.8的异步编程中,我如何使用'yield'语句?

10 浏览
0 Comments

在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(因为它将定义一个异步生成器而不是协程)。那么,我该如何实现相同的功能呢?

0
0 Comments

在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'装饰器也可以实现相同的功能。

0