简体   繁体   English

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

[英]Call an async function in an normal function

I'm pretty new to the asyncio for python 3.6 我对python 3.6的asyncio很新

So the thing is I have a class and I want to init some property in there. 所以问题是我有一个类,我想在那里初始化一些属性。 And one of the property is the return value from an async function. 其中一个属性是异步函数的返回值。

What's the best practice to do this? 这样做的最佳做法是什么?

  1. Call event_loop one time in the init function to get the return value? 在init函数中调用event_loop一次以获取返回值?

  2. Make the __init__ function async? 使__init__函数异步? and run it in the event loop? 并在事件循环中运行它?

Cheers! 干杯!

UPDATE AGAIN: 再次更新:

Following is my code: 以下是我的代码:

 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函数中调用event_loop一次以获取返回值?

If you spin the event loop during __init__ , you won't be able to instantiate C while the event loop is running; 如果在__init__期间旋转事件循环,则在事件循环运行时将无法实例化C ; asyncio event loops don't nest . asyncio事件循环不嵌套

[ EDIT: After the second update to the question, it appears that the event loop gets run by a non-static method C.run , so run_until_complete in __init__ will work with the code as written. [ 编辑:在问题的第二次更新之后,事件循环似乎由非静态方法C.run ,因此__init__ run_until_complete将与编写的代码一起使用。 But that design is limited - for example it doesn't allow constructing another instance of C or of a class like C while the event loop is running.] 但是这种设计是有限的 - 例如,它不允许在事件循环运行时构造C或类C 另一个实例。

Make the __init__ function async? 使__init__函数异步? and run it in the event loop? 并在事件循环中运行它?

__init__ cannot be made async without resorting to very ugly hacks. 如果不诉诸非常丑陋的黑客, __init__不能异步。 Python's __init__ operates by side effect and must return None , whereas an async def function returns a coroutine object. Python的__init__按副作用运行,必须返回None ,而async def函数返回一个协程对象。

To make this work, you have several options: 要使这项工作,你有几个选择:

Async C factory 异步C工厂

Create an async function that returns C instances, such as: 创建一个返回C实例的异步函数,例如:

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

Such a function can be async without problems, and can await as needed. 这样的函数可以没有问题地异步,并且可以根据需要等待。 If you prefer static methods to functions, or if you feel uncomfortable accessing private methods from a function not defined in the class, you can replace make_c() with a C.create() . 如果您更喜欢静态方法到函数,或者如果您感觉不舒服从类中未定义的函数访问私有方法,则可以使用make_c()替换C.create()

Async Cr field 异步Cr字段

You can make the r property asynchronous, simply by storing a Future inside of it: 你可以简单地通过在其中存储Future来使r属性异步:

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

This will require every use of cr to be spelled as await cr . 这将要求每次使用cr拼写为await cr Whether that is acceptable (or even beneficial) will depend on where and how often it is used elsewhere in the program. 这是否可接受(或甚至是有益的)取决于程序中其他地方使用的位置和频率。

Async C constructor 异步C构造函数

Although __init__ cannot be made async, this limitation doesn't apply to its low-level cousin __new__ . 虽然__init__不能异步,但这个限制并不适用于它的低级表亲__new__ T.__new__ may return any object, including one that is not even an instance of T , a fact we can use to allow it to return a coroutine object: 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())

This last approach is something I wouldn't recommend for production code, unless you have a really good reason to use it. 最后一种方法是我不建议用于生产代码的,除非你有充分的理由使用它。

  • It is an abuse of the constructor mechanism, since it defines a C.__new__ constructor that doesn't bother to return a C instance; 这是对构造函数机制的滥用,因为它定义了一个不会返回C实例的C.__new__构造函数;
  • Python will notice the above and will refuse to invoke C.__init__ even if you define or inherit its (sync) implementation; Python将注意到上述内容,即使您定义或继承其(sync)实现,也将拒绝调用C.__init__ ;
  • Using await C() looks very non-idiomatic, even (or especially) to someone used to asyncio. 使用await C()看起来非惯用,甚至(或特别)对于习惯于asyncio的人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM