Как создать ленивое будущее в Python (asyncio), которое можно запустить после создания? - PullRequest
0 голосов
/ 08 июня 2018

Я пытаюсь обернуть голову вокруг asyncio в Python.Я написал эту маленькую программу, которая при вызове будет сначала печатать

Загрузка с сервера

Делать вещи, называемые

, а затем через одну секунду

Async Thingy

Это именно то, что он должен делать, но пока не совсем так, как я этого хочу.

По сути, это имитирует Server, который хочетсоздать PeerPool в __init__, который зависит от ThingThatWeAreWaitingOn.Я хочу иметь возможность создать PeerPool в __init__ и передать Awaitable[ThingThatWeAreWaitingOn], который PeerPool может использовать, как только он будет готов.Опять же, это, кажется, работает просто отлично, но подвох в том, что, как сейчас написано в коде, мы запускаем задачу для решения ThingThatWeAreWaitingOn непосредственно из __init__, но в идеале я хотел бы иметь возможность запустить это изнутри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()

Вот ссылка на работающую демонстрацию: https://repl.it/repls/PleasedHeavenlyLock

1 Ответ

0 голосов
/ 08 июня 2018

Если я все правильно понимаю, вы хотите что-то вроде этого:

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):

будет иметь больше смысла.

Возможно, вам следует создать класс для обнаружения.Вы можете наследовать asyncio.Future или реализовать __await__ магический метод , чтобы сделать его объекты похожими на будущее / ожидаемыми.

...