вызов асинхронной функции в __init__ класса
Есть класс MyClass
. В нем определяем __init__
. Как в __init__
запустить асинхронную функцию? Для усложнения задачи: MyClass
запускается тоже из асинхронных функций. ChatGPT предлагает довольно кривые решения.
async def myFunction():
myClass = MyClass()
async def mySecondFunction():
pass
class MyClass:
def __init__(self):
await mySecondFunction() # Нерабочий вариант
Ответы (2 шт):
__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())
Накидаю больше вариантов:
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 с проверкой инициализации или отдельный метод на ожидание конца инициализации, если это нужно в каких-то ситуациях.