Как написать декоратор, который ведет себя одинаково и с аргументами, и без них, не ломая сигнатуру?

Сделайте первый аргумент функцией по умолчанию None, а настройки — keyword-only. Внутренняя функция создаёт обёртку с functools.wraps, чтобы introspection видел исходные метаданные и сигнатуру.

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

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

Сделайте первый аргумент функцией по умолчанию None, а настройки — keyword-only. Внутренняя функция создаёт обёртку с functools.wraps, чтобы introspection видел исходные метаданные и сигнатуру.

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

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

Сделайте первый аргумент функцией по умолчанию None, а настройки — keyword-only. Внутренняя функция создаёт обёртку с functools.wraps, чтобы introspection видел исходные метаданные и сигнатуру.

from functools import wraps

def trace(func=None, *, label="call"):
    def decorate(fn):
        @wraps(fn)
        def wrapped(*args, **kwargs):
            print(label)
            return fn(*args, **kwargs)
        return wrapped
    return decorate if func is None else decorate(func)

Поддерживаются @trace, @trace() и @trace(label="work"). wraps задаёт __wrapped__, но фактическая сигнатура обёртки остаётся *args, **kwargs. Пример предназначен для обычных функций; для наблюдения за выполнением coroutine нужна async-обёртка с await.

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

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

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

Подробнее