Как сделать один декоратор, работающий и с синхронными, и с асинхронными функциями?

Декоратор может при создании обёртки проверить inspect.iscoroutinefunction и вернуть async wrapper для coroutine-функции, обычный wrapper — для синхронной. Метаданные сохраняют через functools.wraps.

Короткий ответ

Что ответить на собеседовании

Декоратор может при создании обёртки проверить inspect.iscoroutinefunction и вернуть async wrapper для coroutine-функции, обычный wrapper — для синхронной. Метаданные сохраняют через functools.wraps.

Подробный разбор

Ответ с пояснениями

Декоратор может при создании обёртки проверить inspect.iscoroutinefunction и вернуть async wrapper для coroutine-функции, обычный wrapper — для синхронной. Метаданные сохраняют через functools.wraps.

Пример:

from functools import wraps
from inspect import iscoroutinefunction

def decorate(fn):
    if iscoroutinefunction(fn):
        @wraps(fn)
        async def async_wrapper(*args, **kwargs):
            return await fn(*args, **kwargs)
        return async_wrapper

    @wraps(fn)
    def sync_wrapper(*args, **kwargs):
        return fn(*args, **kwargs)
    return sync_wrapper

Общую логику добавляют в обе ветки. Это решение для обычных функций: синхронная функция, возвращающая awaitable, и async generator требуют отдельного контракта, а не угадывания по имени.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее