async_property

Кратко: Python-декораторы для свойств на async-функциях. Поддерживает обычные и кешированные асинхронные свойства (Python 3.7+, MIT).

Источник: async_property

Основная идея

@async_property работает как обычный @property, но на async-функции: доступ к свойству возвращает awaitable.

class Foo:
    @async_property
    async def remote_value(self):
        return await get_remote_value()

instance = Foo()
value = await instance.remote_value

Кешированное свойство

@async_cached_property вызывает функцию один раз. Повторные await возвращают закешированное значение. Кеш защищён asyncio.Lock, чтобы гарантировать однократный вызов при конкурентном доступе.

class Foo:
    @async_cached_property
    async def value(self):
        print('loading value')
        return 123

instance = Foo()
await instance.value  # -> prints 'loading value', returns 123
await instance.value  # -> returns 123 (без повторного вызова)

instance.value = 'abc'  # можно присвоить
del instance.value  # удалить кеш и позволить следующему await заново загрузить

AwaitLoader

Если объект содержит несколько кешированных свойств, можно наследоваться от AwaitLoader — тогда экземпляр становится awaitable и при создании загружает все @async_cached_property параллельно. Также AwaitLoader вызывает await instance.load() если он определён.

class Foo(AwaitLoader):
    async def load(self):
        print('load called')

    @async_cached_property
    async def db_lookup(self):
        return 'success'

    @async_cached_property
    async def api_call(self):
        print('calling api')
        return 'works every time'

instance = await Foo()  # вызовет load() и загрузит кешированные свойства параллельно

Особенности

  • Поддержка обычных и кешированных свойств.
  • Кешированные свойства защищены asyncio.Lock — вызываются только один раз при конкурентных await.
  • Поведение свойств близко к property: можно присваивать и удалять значения.
  • Есть утилиты для массовой предварительной загрузки (AwaitLoader).

Дополнительно