Passing parameter to a python class when initializing it asynchronously

2649 views python
7

I was looking at this answer where they explain how to initialize a class asynchronously with the method __await__. The question is: is it possible to pass parameters when awaiting the initialization of the class, just like when it is initialized synchronously?

In other words, I'd like to be able to do my_class = await MyClass(my_parameter), however I wasn't able to make it work in any way.

Should I just fall back to using the classic __init__ like in this answer?

answered question

1 Answer

11

You should just use __init__. You are creating a regular class instance first, and then await on the instance. These are two separate actions.

For example, you could first create the instance, and then later, separately, await on it:

my_class = MyClass(my_parameter)
result_from_coroutine = await my_class

or you could create a task from it and have the event loop execute it with

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

The __await__ method is what await or the event loop use to drive coroutines. The same separation applies to coroutine functions (defined with async def); they too create a new coroutine object when you call them, and you don't have to await on them immediately. You can use await on the result at another time.

If you are looking for asynchronous instance creation, then you could hack that up by making the __new__ method into a coroutine:

>>> class Async:
...     async def __new__(cls):
...         instance = super().__new__(cls)
...         return instance
...
>>> Async()
<coroutine object Async.__new__ at 0x103654148>

Awaiting on the coroutine would create the actual instance and return it.

Take into account that this does mean that the __init__ method will be skipped; the latter is only called when the __new__ method directly returns an instance of the class (or a subclass), and a coroutine is not such an instance. You'd have to explicitly do so yourself:

class Async:
    async def __new__(cls, *args, **kwargs):
        instance = super().__new__(cls)
        instance.__init__(*args, **kwarg)
        return instance

at which point you could decide to make the __init__ method a coroutine too.

Note that this is really going against the grain. I'd postpone calling dependent coroutines to a later point instead.

For example, you can just store the parameters to the class on the instance and use those when the instance is awaited on (by having __await__ called), exactly as the post you link to advices you to do.

posted this

Have an answer?

JD

Please login first before posting an answer.