DEV Community

Akarshan Gandotra
Akarshan Gandotra

Posted on

Create Awaitable Constructors with AsyncMixin

In this blog post, we'll explore how to create an asynchronous constructor in Python using a mixin called AsyncMixin. This mixin allows you to use an asynchronous __ainit__ method that can await other asynchronous methods during initialization. The __initobj method acts as a crutch to enable proper await behavior when creating instances of the class. I have taken reference from this Slack Overflow answer.

Introduction

In Python, constructors are special methods used to initialize objects when they are created. The traditional constructor, __init__, is synchronous, meaning it cannot await asynchronous tasks. However, there are situations where we may want to perform asynchronous operations during object initialization.

The AsyncMixin class provides an elegant solution to this problem. It introduces an async constructor, __ainit__, which you can use to perform asynchronous operations during object creation. The mixin overrides the __await__ method, allowing proper await behavior when instantiating objects.

Implementation

Below is the code for the AsyncMixin class:

class AsyncMixin:
    def __init__(self, *args, **kwargs):
        """
        Standard constructor used for arguments pass
        Do not override. Use __ainit__ instead
        """
        self.__storedargs = args, kwargs
        self.async_initialized = False

    async def __ainit__(self, *args, **kwargs):
        """Async constructor, you should implement this"""

    async def __initobj(self):
        """Crutch used for __await__ after spawning"""
        assert not self.async_initialized
        self.async_initialized = True
        # pass the parameters to __ainit__ that passed to __init__
        await self.__ainit__(*self.__storedargs[0], **self.__storedargs[1])
        return self

    def __await__(self):
        return self.__initobj().__await__()
Enter fullscreen mode Exit fullscreen mode

How to Use the AsyncMixin

To use the AsyncMixin, follow these steps:

  1. Inherit from AsyncMixin when defining your class.
  2. Implement the async constructor, __ainit__, within your class. This method will contain the asynchronous logic you need during object initialization.
  3. When calling the constructor, use await to create the object asynchronously.

Here's an example of how you can use the AsyncMixin:

class AsyncExample(AsyncMixin):
    async def __ainit__(self, param1, param2):
        # Perform asynchronous operations here
        await self.async_operation1(param1)
        await self.async_operation2(param2)

    async def async_operation1(self, param):
        # Simulate an asynchronous operation
        await asyncio.sleep(1)
        print(f"Async operation 1 with param: {param}")

    async def async_operation2(self, param):
        # Simulate another asynchronous operation
        await asyncio.sleep(2)
        print(f"Async operation 2 with param: {param}")

# Asynchronously create an instance of AsyncExample
async def main():
    obj = await AsyncExample("Parameter 1", "Parameter 2")

# Run the event loop to execute the main coroutine
if __name__ == "__main__":
    import asyncio

    asyncio.run(main())

Enter fullscreen mode Exit fullscreen mode

In this example, we've created a class AsyncExample that inherits from AsyncMixin. The __ainit__ method performs two asynchronous operations (async_operation1 and async_operation2) using await. When creating an instance of AsyncExample, we use the await keyword to asynchronously construct the object and execute the __ainit__ method.

Conclusion

By utilizing the AsyncMixin, you can create asynchronous constructors and await asynchronous methods during object initialization. This pattern is particularly useful when dealing with asynchronous operations in Python, allowing you to write cleaner and more efficient code.

Top comments (2)

Collapse
 
sachingeek profile image
Sachin

Good work. Nice article.

Collapse
 
akarshan profile image
Akarshan Gandotra

Thanks!