简体   繁体   中英

Call an async function in an normal function

I'm pretty new to the asyncio for python 3.6

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?

  2. Make the __init__ function async? 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?

If you spin the event loop during __init__ , you won't be able to instantiate C while the event loop is running; asyncio event loops don't nest .

[ 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. 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.]

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

__init__ cannot be made async without resorting to very ugly hacks. Python's __init__ operates by side effect and must return None , whereas an async def function returns a coroutine object.

To make this work, you have several options:

Async C factory

Create an async function that returns C instances, such as:

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() .

Async Cr field

You can make the r property asynchronous, simply by storing a Future inside of it:

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 . Whether that is acceptable (or even beneficial) will depend on where and how often it is used elsewhere in the program.

Async C constructor

Although __init__ cannot be made async, this limitation doesn't apply to its low-level cousin __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:

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;
  • Python will notice the above and will refuse to invoke C.__init__ even if you define or inherit its (sync) implementation;
  • Using await C() looks very non-idiomatic, even (or especially) to someone used to asyncio.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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