❌ Ошибка 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):
...