🐛 Типичные ошибки

⚡ Частые ловушки

  • Возвращать результат вызова обёртки вместо самой обёртки.
  • Забывать *args и **kwargs в обёртке.
  • Пропускать уровень фабрики в параметризованном декораторе.
  • Добавлять троеточие поверх среза, превышая лимит.
  • Проверять тип через type() вместо isinstance().
  • Использовать изменяемый объект как значение по умолчанию.

❌ Ошибка 1: не вызвать обёртку

# Неправильно
def my_decorator(func):
    def wrapper():
        print("Before")
        func()
        print("After")
    return wrapper()  # возвращается результат вызова, а не функция

Сообщение об ошибке: TypeError: 'NoneType' object is not callable при попытке вызвать декорированную функцию.

Почему возникает: декоратор должен возвращать функцию-обёртку, а не результат её вызова.

✅ Исправление

def my_decorator(func):
    def wrapper():
        print("Before")
        func()
        print("After")
    return wrapper  # возвращаем функцию

❌ Ошибка 2: обёртка не принимает аргументы

# Неправильно
def log_to_file(func):
    def wrapper():           # забыли *args, **kwargs
        result = func()
        logging.info(...)
        return result
    return wrapper

@log_to_file
def add(a, b):
    return a + b

add(5, 3)  # TypeError: wrapper() takes 0 positional arguments but 2 were given

Сообщение об ошибке: TypeError: wrapper() takes 0 positional arguments but 2 were given.

Почему возникает: обёртка должна быть универсальной и принимать любые аргументы целевой функции.

✅ Исправление

def log_to_file(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        logging.info(...)
        return result
    return wrapper

❌ Ошибка 3: пропущен уровень в параметризованном декораторе

# Неправильно
def limit_output(max_len):
    def wrapper(func):
        result = func()
        ...
        return result
    return wrapper

Сообщение об ошибке: функция вызывается сразу при декорировании или результат не оборачивается.

Почему возникает: параметризованный декоратор требует трёх уровней: фабрика → декоратор → обёртка.

✅ Исправление

def limit_output(max_len):
    def decorator(func):
        def wrapper():
            result = func()
            ...
            return result
        return wrapper
    return decorator

❌ Ошибка 4: обрезка короче, чем троеточие

# Неправильно
def truncate_wrong(result: str, max_len: int) -> str:
    return result[:max_len] + "..."

Почему возникает: итоговая строка получается длиннее max_len, потому что троеточие добавляется поверх среза.

✅ Исправление

def truncate(result: str, max_len: int) -> str:
    return result[:max_len - 3] + "..."

❌ Ошибка 5: проверка типов через type()

# Неправильно
def check_type_wrong(arg, expected):
    if type(arg) is not expected:
        pass

Почему возникает: сравнение через type() не учитывает наследование. Если передан экземпляр подкласса, проверка не сработает.

✅ Исправление

def check_type(arg, expected):
    if not isinstance(arg, expected):
        pass

❌ Ошибка 6: изменяемый объект по умолчанию в декораторе

# Неправильно
def cache(func, memo={}):
    def wrapper(*args):
        if args not in memo:
            memo[args] = func(*args)
        return memo[args]
    return wrapper

Почему возникает: изменяемый аргумент по умолчанию создаётся один раз при определении функции и разделяется между всеми декорированными функциями.

✅ Исправление

def cache(func):
    memo = {}
    def wrapper(*args):
        ...