如何在 Python (asyncio) 中创建一个可以在创建后启动的惰性 Future?

How do I create a lazy Future in Python (asyncio) that can be kicked off after creation?

我正试图在 Python 中围绕 asyncio 思考。我写了这个小程序,调用时会先打印

Server booting

Do stuff called

一秒后

Async Thingy

这正是它应该做的,但还不是我想要的方式。

基本上这模仿了 Server 想要在 __init__ 中创建一个 PeerPool,这取决于 ThingThatWeAreWaitingOn。我希望能够在 __init__ 中创建 PeerPool 并传递一个 Awaitable[ThingThatWeAreWaitingOn] 以便 PeerPool 可以在准备就绪后立即使用。同样,这似乎工作得很好,但要注意的是,正如现在的代码一样,我们开始直接从 __init__ 中解决 ThingThatWeAreWaitingOn 的任务,但理想情况下我希望能够踢从 run().

内部关闭

我该怎么做?

import asyncio
from typing import (
    Awaitable,
    Any
)

class ThingThatWeAreWaitingOn():
    name = "Async Thingy"

class PeerPool():

    def __init__(self, discovery: Awaitable[ThingThatWeAreWaitingOn]):
        self.awaitable_discovery = discovery

    def do_stuff(self):
        print("Do stuff called")
        self.awaitable_discovery.add_done_callback(lambda d: print(d.result().name))

class Server():

    def __init__(self):
        # This immediately kicks of the async task but all I want is to 
        # create a Future to pass that would ideally be kicked off in
        # the run() method
        self.fut_discovery = asyncio.ensure_future(self.get_discovery())
        self.peer_pool = PeerPool(self.fut_discovery)
    
    async def get_discovery(self):
        await asyncio.sleep(1)
        return ThingThatWeAreWaitingOn()

    def run(self):
        loop = asyncio.get_event_loop()
        print("Server booting")

        # Here is where I want to "kick off" the self.fut_discovery but how?
        # self.fut_discovery.kick_off_now()

        self.peer_pool.do_stuff()

        loop.run_forever()

server = Server()
server.run()

这是一个 link 可运行的演示:https://repl.it/repls/PleasedHeavenlyLock

如果我没看错的话,你想要这样的东西:

class Server():    
    def __init__(self):
        self.fut_discovery = asyncio.Future()
        self.peer_pool = PeerPool(self.fut_discovery)

    async def get_discovery(self):
        await asyncio.sleep(1)
        return ThingThatWeAreWaitingOn()

    def run(self):
        loop = asyncio.get_event_loop()
        print("Server booting")

        async def discovery_done():
            res = await self.get_discovery()
            self.fut_discovery.set_result(res)
        asyncio.ensure_future(discovery_done())  # kick discovery to be done

        self.peer_pool.do_stuff()
        loop.run_forever()

您可能想以某种方式重写代码以使其更清晰。现在还不是很清楚你要做什么以及代码的哪一部分取决于哪一部分。

例如,awaitable_discovery 名称具有误导性:普通的 awaitable 没有必要具有 add_done_callback 方法。如果您打算使用此方法,请签名

class PeerPool():
    def __init__(self, fut_discovery: asyncio.Future):

会更有意义。

也许您应该创建 class 以供发现。您可以继承 asyncio.Future 或实现 __await__ magic method 使其成为对象 future-like/awaitable.