Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using async await in Python __init__ method

I am writing a class and want to use an async function in the __init__ method to set up some variables needed for the class. The problem is, I can't do that, because __init__ has to be synchronous.

Here is the relevant part of my code (edited for simplicity, the logic remains the same):

# This has to be called outside of the class
asyncDatabaseConnection = startDBConnection()

class discordBot(discord.Client):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Init is only run once, but we cant use async stuff here
        self.firstRun = True

    async def on_ready(self):
        # Other stuff happens here but it doesen't involve this question

        # on_ready is called when bot is ready, but can be called multiple times when running
        # (if bot has to reconnect to API), so we have to check
        if self.firstRun:
            await asyncDatabaseConnection.setValue("key", "value")
            self.firstRun = False

if __name__ == "__main__":
    # Instance class and start async stuff
    bot = discordBot()
    bot.run()

As you can see, it is for a Discord bot, but that doesn't really matter, it's more about the logic.

The function I want to call is asyncDatabaseConnection.setValue("key", "value").

Like I said, I cannot call it from __init__ because __init__ has to be synchronous, so instead I set firstRun to True during the init call, and then I can use that later on to tell if the code has been run before or not

on_ready is a function that is called when the bot is ready to start sending/receiving data, so I can use it as sort of a second __init__. The problem comes from the fact that on_ready can be called multiple times throughout the running of the program, meaning I have to have the firstRun check I described before.

This seems like a lot of code just to do 1 thing at startup (as well as adding overhead, however small, when on_ready is called). Is there a cleaner way of doing this?

like image 545
psidex Avatar asked Mar 31 '26 00:03

psidex


1 Answers

It's a little awkward, but you can create a Task, then run it and get its result. If you do this often, it may help to write a helper function:

def run_and_get(coro):
    task = asyncio.create_task(coro)
    asyncio.get_running_loop().run_until_complete(task)
    return task.result()

class discordBot(discord.Client):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        run_and_get(asyncDatabaseConnection.setValue("key", "value"))

This depends on there being a running event loop, which I believe Client.__init__ sets up

like image 89
Patrick Haugh Avatar answered Apr 02 '26 14:04

Patrick Haugh



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!