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

标签 python python-3.x asynchronous 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()

最佳答案

Call event_loop one time in the init function to get the return value?

如果您在__init__ 期间旋转事件循环,您将无法在事件循环运行时实例化C;异步事件循环 don't nest .

[编辑:在问题的第二次更新之后,事件循环似乎是由非静态方法 C.run 运行的,所以 __init__ 中的 run_until_complete 将使用编写的代码。但是这种设计是有限的——例如,当事件循环是正在运行。]

Make the __init__ function async? and run it in the event loop?

__init__ 不借助非常丑陋的 hack 就无法实现异步。 Python 的 __init__ 通过副作用运行并且必须返回 None,而 async def 函数返回协程对象。

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

异步 C 工厂

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

async def make_c():
    c = C()
    await c._async_init()
    return c

这样的函数可以毫无问题地异步,并且可以根据需要等待。如果您更喜欢静态方法而不是函数,或者您觉得从类中未定义的函数访问私有(private)方法感到不舒服,您可以将 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'))

这将要求每次使用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())

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

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

关于python - 在普通函数中调用异步函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49494299/

相关文章:

python - 如何在Python中搜索一组字符串中的一个

python - Matplotlib 饼图与 'All Other Categories"

.net - 如何在F#中处理ValueTask <T>?

java - 必须为元素类型 "xmlns"声明属性 "beans"

python - 如何获得 apport sys.excepthook 的根目录?

python - 我创建了一个有多个敌人的游戏 space invaders

python - 如何将字数列表转换为数据框以进行情感分析

python - 在 Python 3 中增加字节数组?

json - 将嵌套字典合并到 d3 sunburst 的 Json 文件

c# - 对套接字的异步方法 BeginSend 的队列调用