异步初始化时将参数传递给python类

Posted

技术标签:

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

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

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

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

【问题讨论】:

【参考方案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__ 实际上是微不足道的,但您希望返回的实例是可等待的,则不需要拆分初始化的额外复杂性。

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

【讨论】:

我会玩这个。谢谢 @FedericoCorazza 对被拒绝的编辑感到抱歉,我不知道它是由提出问题的同一个人完成的。我现在修改了答案,提到了asyncio.run的临时状态。

以上是关于异步初始化时将参数传递给python类的主要内容,如果未能解决你的问题,请参考以下文章

使用它时将参数传递给perl包

将参数传递给 Node.js 异步瀑布

您可以在创建时将参数传递给 AngularJS 控制器吗?

您可以在创建时将参数传递给 AngularJS 控制器吗?

将命令行参数传递给python脚本[重复]

nodemon :作为必需模块使用时将参数传递给可执行文件