我怎样才能在未来的对象__await__中等待?

Mik*_*mov 28 python python-3.x async-await python-asyncio

PEP 0492增加了新的__await__魔法.实现此方法的对象变为类似未来的对象,可以等待使用await.很明显:

import asyncio


class Waiting:
    def __await__(self):
        yield from asyncio.sleep(2)
        print('ok')

async def main():
    await Waiting()

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
Run Code Online (Sandbox Code Playgroud)

好的,但如果我想调用一些已async def定义的函数而不是asyncio.sleep?我不能使用await因为__await__不是async函数,我无法使用yield from因为本机协同程序需要await表达式:

async def new_sleep():
    await asyncio.sleep(2)

class Waiting:
    def __await__(self):
        yield from new_sleep()  # this is TypeError
        await new_sleep()  # this is SyntaxError
        print('ok')
Run Code Online (Sandbox Code Playgroud)

我该如何解决?

And*_*lov 33

使用直接__await__()电话:

async def new_sleep():
    await asyncio.sleep(2)

class Waiting:
    def __await__(self):
        return new_sleep().__await__()
Run Code Online (Sandbox Code Playgroud)

该解决方案由Yury Selivanov(PEP 492的作者)推荐用于aioodbc库

  • 这个答案:/sf/answers/3270555081/ 有点更一般,因为它允许等待多个可等待项 (2认同)

Sil*_*eak 15

短版:await foo可以替换为yield from foo.__await__()


结合其他答案的所有想法 -

在最简单的情况下,只需委托另一个等待的作品:

def __await__(self):
    return new_sleep().__await__()
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为该__await__方法返回一个迭代器(参见PEP 492),因此返回另一个__await__迭代器是正常的.

当然,这意味着我们根本无法改变原始等待的暂停行为.更通用的方法是镜像await关键字并使用yield from- 这使我们可以将多个等待的迭代器组合成一个:

def __await__(self):
    # theoretically possible, but not useful for my example:
    #yield from something_else_first().__await__()
    yield from new_sleep().__await__()
Run Code Online (Sandbox Code Playgroud)

这里有一个问题:这与第一个变体没有完全相同!yield from是一个表达式,所以要做到和以前完全一样,我们还需要返回该值:

def __await__(self):
    return (yield from new_sleep().__await__())
Run Code Online (Sandbox Code Playgroud)

这直接反映了我们如何使用await语法编写正确的委托:

    return await new_sleep()
Run Code Online (Sandbox Code Playgroud)

额外的一点 - 这两者有什么区别?

def __await__(self):
    do_something_synchronously()
    return new_sleep().__await__()

def __await__(self):
    do_something_synchronously()
    return (yield from new_sleep().__await__())
Run Code Online (Sandbox Code Playgroud)

第一个变量是一个普通函数:当你调用它时,do_...执行它并返回一个迭代器.第二个是发电机功能; 调用它根本不执行任何代码!只有在第一次生成返回的迭代器时才会do_...执行.这在以下方面有所不同,有点人为的情况:

def foo():
    tmp = Waiting.__await__()
    do_something()
    yield from tmp
Run Code Online (Sandbox Code Playgroud)


crv*_*rvv 6

要在__await__函数内部等待,请使用以下代码:

async def new_sleep():
    await asyncio.sleep(1)


class Waiting:
    def __await__(self):
        yield from new_sleep().__await__()
        print('first sleep')
        yield from new_sleep().__await__()
        print('second sleep')
        return 'done'
Run Code Online (Sandbox Code Playgroud)


Mik*_*mov 5

我不明白为什么我不能从本地协同程序产生里面__await__,不过貌似有可能从发电机产生协程内部__await__收益率从本地协同程序里面产生协同程序。有用:

async def new_sleep():
    await asyncio.sleep(2)

class Waiting:
    def __await__(self):
        @asyncio.coroutine
        def wrapper(coro):
            return (yield from coro)
        return (yield from wrapper(new_sleep()))
Run Code Online (Sandbox Code Playgroud)


Hua*_*Gao 5

使用装饰器。

def chain__await__(f):
    return lambda *args, **kwargs: f(*args, **kwargs).__await__()
Run Code Online (Sandbox Code Playgroud)

然后编写__await__为原生协程。

async def new_sleep():
    await asyncio.sleep(2)

class Waiting:
    @chain__await__
    async def __await__(self):
        return await new_sleep()
Run Code Online (Sandbox Code Playgroud)