← Назад к вопросам

Что будет, если декоратор ничего не вернет в Python?

2.0 Middle🔥 251 комментариев
#DevOps и инфраструктура#Django

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что Произойдёт, Если Декоратор Ничего Не Вернет

Если декоратор не вернёт ничего (явно не вернёт функцию или значение), то исходная функция будет заменена на None. Это один из самых распространённых баг-источников при работе с декораторами в Python.

Базовый Пример Проблемы

# ❌ НЕПРАВИЛЬНО: Декоратор не возвращает функцию
def broken_decorator(func):
    def wrapper(*args, **kwargs):
        print("Before function call")
        func(*args, **kwargs)  # Вызываем функцию, но не возвращаем результат
        print("After function call")
        # return wrapper  ← ЗАБЫЛИ ВЕРНУТЬ!
    # return wrapper  ← ЗАБЫЛИ ВЕРНУТЬ!

@broken_decorator
def greet(name):
    return f"Hello, {name}!"

# Попытка вызвать функцию
result = greet("Alice")
print(result)  # Output: None (не "Hello, Alice!")
print(type(greet))  # Output: <class NoneType>

Почему это происходит? Python заменяет исходную функцию на результат декоратора:

# Декоратор работает так:
greet = broken_decorator(greet)
# Так как broken_decorator ничего не вернул, greet = None

Правильная Реализация Декоратора

# ✅ ПРАВИЛЬНО: Декоратор возвращает wrapper функцию
def proper_decorator(func):
    def wrapper(*args, **kwargs):
        print("Before function call")
        result = func(*args, **kwargs)  # Вызываем и СОХРАНЯЕМ результат
        print("After function call")
        return result  # ← ВАЖНО: Возвращаем результат
    return wrapper  # ← ВАЖНО: Возвращаем wrapper функцию

@proper_decorator
def greet(name):
    return f"Hello, {name}!"

result = greet("Alice")
print(result)  # Output: Hello, Alice!
print(type(greet))  # Output: <class function>

Различие в Поведении

# Полное сравнение
print("НЕПРАВИЛЬНЫЙ ДЕКОРАТОР:")
print(f"greet = {greet}")
print(f"type(greet) = {type(greet)}")
# Output:
# greet = None
# type(greet) = <class NoneType>

print("\nПРАВИЛЬНЫЙ ДЕКОРАТОР:")
print(f"greet = {greet}")
print(f"type(greet) = {type(greet)}")
# Output:
# greet = <function proper_decorator.<locals>.wrapper at 0x...>
# type(greet) = <class function>

Частые Ошибки

Ошибка 1: Забыли return в wrapper функции

# ❌ Неправильно
def logging_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        func(*args, **kwargs)  # Вызвали, но не вернули результат
    return wrapper

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

result = add(2, 3)
print(result)  # Output: None, а не 5!

Ошибка 2: Забыли return в конце декоратора

# ❌ Неправильно
def timing_decorator(func):
    import time
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)  # Правильно вернули результат
        end = time.time()
        print(f"Execution time: {end - start}s")
        return result
    # ЗАБЫЛИ return wrapper!

@timing_decorator
def slow_function():
    time.sleep(1)
    return "Done"

slow_function()  # TypeError: NoneType object is not callable

Правильная Структура Декоратора

def complete_decorator(func):
    def wrapper(*args, **kwargs):
        # 1. До выполнения функции
        print("Before")
        
        # 2. Вызываем функцию и СОХРАНЯЕМ результат
        result = func(*args, **kwargs)
        
        # 3. После выполнения функции
        print("After")
        
        # 4. ОБЯЗАТЕЛЬНО возвращаем результат
        return result
    
    # 5. ОБЯЗАТЕЛЬНО возвращаем wrapper функцию
    return wrapper

Декораторы с Параметрами

Частая ошибка при многоуровневых декораторах:

# ❌ Неправильно
def parametrized_decorator(message):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(f"Message: {message}")
            return func(*args, **kwargs)
        return wrapper
    # ЗАБЫЛИ return decorator!

@parametrized_decorator("Hello")
def greet():
    return "Hi"

greet()  # TypeError: NoneType object is not callable
# ✅ Правильно
def parametrized_decorator(message):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(f"Message: {message}")
            return func(*args, **kwargs)
        return wrapper
    return decorator  # ← ПРАВИЛЬНО

@parametrized_decorator("Hello")
def greet():
    return "Hi"

greet()  # Output: Message: Hello, Hi

Использование functools.wraps

Одна из важных практик — сохранение метаданных исходной функции:

from functools import wraps

# ❌ Без wraps
def bad_decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@bad_decorator
def my_function():
    """My function docstring."""
    pass

print(my_function.__name__)  # Output: wrapper (потеряем имя функции)
print(my_function.__doc__)   # Output: None (потеряем документацию)

# ✅ С wraps
def good_decorator(func):
    @wraps(func)  # Копирует __name__, __doc__, __annotations__ и другие метаданные
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@good_decorator
def my_function():
    """My function docstring."""
    pass

print(my_function.__name__)  # Output: my_function
print(my_function.__doc__)   # Output: My function docstring.

Практический Пример: Декоратор для Обработки Ошибок

from functools import wraps
import logging

def error_handler(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        try:
            result = func(*args, **kwargs)
            return result  # ← ВАЖНО: возвращаем результат
        except Exception as e:
            logging.error(f"Error in {func.__name__}: {e}")
            return None  # Или re-raise, или обработать иначе
    return wrapper  # ← ВАЖНО: возвращаем wrapper

@error_handler
def divide(a, b):
    return a / b

print(divide(10, 2))    # Output: 5.0
print(divide(10, 0))    # Output: None (error обработана)

Отладка: Как Понять, Что Декоратор Сломан

def my_decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    # Забыли return wrapper!

@my_decorator
def test():
    return "test"

# Признаки проблемы:
print(test)  # <class NoneType> вместо <class function>
try:
    test()  # TypeError: NoneType object is not callable
except TypeError as e:
    print(f"Error: {e}")

Заключение

Если декоратор не вернёт функцию (wrapper), то исходная функция будет заменена на None, что приведёт к:

  1. Потере функциональности — функция больше не вызывается
  2. TypeError при попытке вызвать функцию
  3. Сложной отладке — ошибка может обнаружиться далеко от источника

Ключевые правила:

  • Wrapper функция ДОЛЖНА возвращать результат исходной функции
  • Декоратор ДОЛЖЕН возвращать wrapper функцию
  • Используй @functools.wraps для сохранения метаданных
  • Всегда тестируй декораторы!