【问题标题】:Passing parameter to a python class when initializing it asynchronously异步初始化时将参数传递给python类
【发布时间】:2018-09-16 22:29:39
【问题描述】:

我在看this answer,他们解释了如何使用__await__ 方法异步初始化一个类。问题是:在等待类的初始化时,是否可以像同步初始化一样传递参数?

换句话说,我希望能够做到 my_class = await MyClass(my_parameter),但是我无法以任何方式使其工作。

我是否应该像this answer 那样回退到使用经典的__init__

【问题讨论】:

    标签: python python-3.x async-await python-asyncio


    【解决方案1】:

    您应该只使用__init__。您正在创建一个常规类实例首先,然后在该实例上等待。这是两个独立的操作。

    例如,您可以先创建实例,然后单独等待它:

    my_class = MyClass(my_parameter)
    result_from_coroutine = await my_class
    

    或者您可以从中创建一个任务并让事件循环执行它

    my_class = MyClass(my_parameter)
    task = asyncio.create_task(my_class)  # the loop will await the task
    # ...
    if task.done():
        result_from_coroutine = task.result()
    

    __await__ 方法是 await 或事件循环用来驱动协程的方法。同样的分离也适用于协程函数(用async def 定义);当您调用它们时,它们也会创建一个新的协程对象,您不必立即等待它们。您可以在其他时间对结果使用await

    如果您正在寻找异步实例创建,那么您可以通过将__new__ method 变成协程来解决这个问题:

    >>> class Async:
    ...     async def __new__(cls):
    ...         instance = super().__new__(cls)
    ...         return instance
    ...
    >>> Async()
    <coroutine object Async.__new__ at 0x103654148>
    

    等待协程将创建实际实例并返回它。

    考虑到这确实意味着__init__ 方法被跳过;后者仅在__new__方法直接返回类(或子类)的实例时调用,协程不是这样的实例。您必须自己明确地这样做:

    class Async:
        async def __new__(cls, *args, **kwargs):
            instance = super().__new__(cls)
            instance.__init__(*args, **kwarg)
            return instance
    

    此时您可以决定将__init__ 方法也设为协程。

    请注意,这确实违反了规定。我会把调用依赖协程推迟到以后。

    例如,您可以将参数存储到实例上的类中,并在等待实例时使用这些参数(通过调用__await__),就像您链接到建议的帖子一样。

    【讨论】:

    • 我之所以进行异步初始化是因为我正在等待一些方法,这些方法需要在创建类后立即运行,但是因为它们是私有方法,所以我没有想把它们称为类本身的“外部”。
    • @FedericoCorazza:你不能异步创建实例。
    • @FedericoCorazza:那是因为创建实例的过程不是由__init__处理的,这只是一个名为实例已经创建之后的钩子。
    • 这很有趣,但我同意,我应该稍后再调用 awaitables。
    【解决方案2】:

    换句话说,我希望能够做到my_class = await MyClass(my_parameter),但是我无法以任何方式使其工作。

    您可以通过实现__await__ 使MyClass 可等待:

    class MyClass:
        def __init__(self, delay):
            self.delay = delay
    
        async def __await__(self):
            await asyncio.sleep(self.delay)
    
    asyncio.run(MyClass(2)) # sleeps 2 seconds
    

    链接答案中的代码做了类似的事情,但它更复杂,因为它假设__init__ 本身需要await。如果不是这种情况,并且您的 __init__ 实际上是微不足道的,但您希望返回的实例是可等待的,则不需要拆分初始化的额外复杂性。

    注意:尽管是recommendedasyncio.run() 仍被标记为临时的。在上面的例子中,它可以很容易地替换为run_until_complete

    【讨论】:

    • 我会玩这个。谢谢
    • @FedericoCorazza 对被拒绝的编辑感到抱歉,我不知道它是由提出问题的同一个人完成的。我现在修改了答案,提到了asyncio.run的临时状态。
    猜你喜欢
    • 1970-01-01
    • 2020-09-26
    • 2020-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多