вызов асинхронной функции в __init__ класса

Есть класс MyClass. В нем определяем __init__. Как в __init__ запустить асинхронную функцию? Для усложнения задачи: MyClass запускается тоже из асинхронных функций. ChatGPT предлагает довольно кривые решения.

async def myFunction():
    myClass = MyClass()

async def mySecondFunction():
    pass

class MyClass:
    def __init__(self):
        await mySecondFunction()  # Нерабочий вариант

Ответы (2 шт):

Автор решения: Stanislav Volodarskiy

__init__ вызываете не вы, а интерпретатор. Вызов синхронный, с этим сделать ничего нельзя. Но можно обернуть конструирование:

async def myFunction():
    myClass = await MyClass.make()


async def mySecondFunction():
    pass


class MyClass:
    def __init__(self):
        pass

    @classmethod    
    async def make(cls):
        self = cls()
        await mySecondFunction()
        return self

Наведу красоту. Декоратор async_init оборачивает объявление класса в функцию с тем же именем. Функция асинхронная. Она синхронно вызывает конструктор и асинхронно метод self.post_init(). В вызывающем коде всё выглядит как класс с асинхронным конструктором.

import asyncio


def async_init(cls):

    async def factory(*args, **kwargs):
        self = cls(*args, **kwargs)
        await self.post_init()

    return factory


async def mySecondFunction():
    print('in mySecondFunction')


@async_init
class MyClass:
    def __init__(self):
        pass

    async def post_init(self):
        await mySecondFunction()


async def main():
    myClass = await MyClass()


asyncio.run(main())
→ Ссылка
Автор решения: AivanF.

Накидаю больше вариантов:

1. Собственный конструктор через метод класса

class MyClass:
    def __init__(self, data):
        self.data = data

    @classmethod
    async def create(cls):
        data = await some_async_function()
        return cls(data)

async def main():
    instance = await MyClass.create()

Аналогично можно сделать со статическим методом или отдельной функцией.

2. Самостоятельный вызов функции асинк иницииазации

class MyClass:
    def __init__(self):
        self.data = None

    async def initialize(self):
        self.data = await some_async_function()

async def main():
    instance = MyClass()
    await instance.initialize()

3. Отложенная инициализация

Если ожидать инициализации не нужно, можно её вызывать в фоне, при этом уже имея объект:

import asyncio

class MyClass:
    def __init__(self):
        self.data = None
        asyncio.create_task(self.initialize())

    async def initialize(self):
        self.data = await some_async_function()

async def main():
    instance = MyClass()

Можно добавить property с проверкой инициализации или отдельный метод на ожидание конца инициализации, если это нужно в каких-то ситуациях.

→ Ссылка