在普通函数中调用异步函数

qic*_*_he 5 python asynchronous python-3.x python-asyncio

我对python 3.6的asyncio相当陌生

所以事情是我有一堂课,我想在那儿初始化一些属性。属性之一是异步函数的返回值。

最佳做法是什么?

  1. 在init函数中一次调用event_loop以获得返回值?

  2. 使__init__函数异步?并在事件循环中运行它?

干杯!

再次更新:

以下是我的代码:

import asyncio
import aioredis
from datetime import datetime

class C:
    def __init__(self):
        self.a = 1
        self.b = 2
        self.r = None
        asyncio.get_event_loop().run_until_complete(self._async_init())

    async def _async_init(self):
        # this is the property I want, which returns from an async function
        self.r = await aioredis.create_redis('redis://localhost:6379')

    async def heart_beat(self):
        while True:
            await self.r.publish('test_channel', datetime.now().__str__())
            await asyncio.sleep(10)

    def run(self):
        asyncio.get_event_loop().run_until_complete(self.heart_beat())

c=C()
c.run()
Run Code Online (Sandbox Code Playgroud)

use*_*342 7

在init函数中调用一次event_loop来获取返回值?

如果您在 期间旋转事件循环__init__,您将无法C在事件循环运行时实例化;asyncio 事件循环不嵌套

[编辑:第二次更新的问题后,似乎事件循环获得由非静态方法运行C.run,所以run_until_complete__init__将工作与代码编写。但是这种设计是有限的——例如,它不允许像在事件循环运行时那样构造另一个C类或类的实例C。]

使__init__函数异步?并在事件循环中运行它?

__init__如果不诉诸非常丑陋的黑客,就不能使异步。Python 的__init__操作有副作用并且必须 return None,而async def函数返回一个协程对象。

要完成这项工作,您有多种选择:

异步C工厂

创建一个返回C实例的异步函数,例如:

async def make_c():
    c = C()
    await c._async_init()
    return c
Run Code Online (Sandbox Code Playgroud)

这样的函数可以毫无问题地异步,并且可以根据需要等待。如果您更喜欢静态方法而不是函数,或者如果您对从未在类中定义的函数访问私有方法感到不舒服,则可以替换make_c()C.create().

异步C.r字段

您可以使r属性异步,只需在其中存储一个Future

class C:
    def __init__(self):
        self.a = 1
        self.b = 2
        loop = asyncio.get_event_loop()
        # note: no `await` here: `r` holds an asyncio Task which will
        # be awaited (and its value accessed when ready) as `await c.r`
        self.r = loop.create_task(aioredis.create_redis('redis://localhost:6379'))
Run Code Online (Sandbox Code Playgroud)

这将要求每次使用的c.r拼写为await c.r。这是否可以接受(甚至有益)将取决于它在程序中的其他地方使用的位置和频率。

异步C构造函数

虽然__init__不能被设为异步,但这个限制不适用于它的低级表亲__new__T.__new__可以返回任何对象,包括一个甚至不是 实例的对象,T我们可以使用这一事实来允许它返回一个协程对象:

class C:
    async def __new__(cls):
        self = super().__new__(cls)
        self.a = 1
        self.b = 2
        self.r = await aioredis.create_redis('redis://localhost:6379')
        return self

# usage from another coroutine
async def main():
    c = await C()

# usage from outside the event loop
c = loop.run_until_complete(C())
Run Code Online (Sandbox Code Playgroud)

我不建议将最后一种方法用于生产代码,除非您有充分的理由使用它。

  • 这是对构造函数机制的滥用,因为它定义了一个C.__new__不费心返回C实例的构造函数;
  • Python会注意到上述情况,C.__init__即使你定义或继承了它的(同步)实现,它也会拒绝调用;
  • 使用await C()看起来非常不习惯,甚至(或特别是)对于习惯 asyncio 的人来说也是如此。