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

Как выглядит непараметрический декоратор?

1.7 Middle🔥 141 комментариев
#Soft Skills

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

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

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

Непараметрический декоратор в Python

Непараметрический декоратор — это декоратор, который не принимает аргументы. Это базовая и наиболее простая форма декораторов в Python. В отличие от параметрических декораторов, непараметрические декораторы применяются напрямую к функции или классу.

Базовая структура непараметрического декоратора

Непараметрический декоратор состоит из двух слоёв функций:

def my_decorator(func):
    """Непараметрический декоратор"""
    def wrapper(*args, **kwargs):
        # Код, выполняемый ДО вызова функции
        print(f'Calling {func.__name__}')
        
        # Вызов исходной функции
        result = func(*args, **kwargs)
        
        # Код, выполняемый ПОСЛЕ вызова функции
        print(f'Finished {func.__name__}')
        
        return result
    
    return wrapper

# Применение декоратора
@my_decorator
def greet(name):
    return f'Hello, {name}!'

# Использование
print(greet('Alice'))
# Вывод:
# Calling greet
# Finished greet
# Hello, Alice!

Как это работает

# Эквивалентные записи:

# 1. С использованием декоратора
@my_decorator
def greet(name):
    return f'Hello, {name}!'

# 2. Без декоратора (явный вызов)
greet = my_decorator(greet)

# В обоих случаях переменная greet указывает на функцию wrapper

Практические примеры непараметрических декораторов

1. Декоратор для логирования

import logging
from functools import wraps

logger = logging.getLogger(__name__)

def log_calls(func):
    """Логирует вызовы функции"""
    @wraps(func)  # Сохраняет метаданные исходной функции
    def wrapper(*args, **kwargs):
        logger.info(f'Calling {func.__name__} with args={args}, kwargs={kwargs}')
        try:
            result = func(*args, **kwargs)
            logger.info(f'{func.__name__} returned {result}')
            return result
        except Exception as e:
            logger.error(f'{func.__name__} raised {type(e).__name__}: {e}')
            raise
    
    return wrapper

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

add(2, 3)  # Все вызовы будут залогированы

2. Декоратор для измерения времени выполнения

import time
from functools import wraps

def timeit(func):
    """Измеряет время выполнения функции"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        elapsed = time.perf_counter() - start
        print(f'{func.__name__} took {elapsed:.4f}s')
        return result
    
    return wrapper

@timeit
def slow_function():
    time.sleep(2)
    return 'Done'

slow_function()  # slow_function took 2.0001s

3. Декоратор для кеширования результатов

from functools import wraps

def cache(func):
    """Кеширует результаты функции"""
    cached_results = {}
    
    @wraps(func)
    def wrapper(*args):
        if args in cached_results:
            print(f'Returning cached result for {args}')
            return cached_results[args]
        
        result = func(*args)
        cached_results[args] = result
        return result
    
    return wrapper

@cache
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

fibonacci(10)  # Использует кеш для оптимизации

4. Декоратор для валидации аргументов

from functools import wraps

def validate_positive(func):
    """Валидирует, что все аргументы положительные"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        for arg in args:
            if isinstance(arg, (int, float)) and arg < 0:
                raise ValueError(f'Expected positive number, got {arg}')
        
        for key, value in kwargs.items():
            if isinstance(value, (int, float)) and value < 0:
                raise ValueError(f'Expected positive number for {key}, got {value}')
        
        return func(*args, **kwargs)
    
    return wrapper

@validate_positive
def square_root(n):
    return n ** 0.5

square_root(16)  # OK: 4.0
square_root(-16)  # Raises ValueError

5. Декоратор для обработки исключений

from functools import wraps

def handle_errors(func):
    """Обрабатывает исключения функции"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except KeyError as e:
            print(f'KeyError: {e}')
            return None
        except ValueError as e:
            print(f'ValueError: {e}')
            return None
        except Exception as e:
            print(f'Unexpected error: {type(e).__name__}: {e}')
            raise
    
    return wrapper

@handle_errors
def get_dict_value(d, key):
    return d[key]

get_dict_value({}, 'missing_key')  # Печатает KeyError, возвращает None

Декоратор с использованием functools.wraps

ВАЖНО: Всегда используй @wraps из functools!

from functools import wraps

# ❌ Плохо — теряются метаданные функции
def bad_decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

# ✅ Хорошо — сохраняются метаданные
def good_decorator(func):
    @wraps(func)  # Копирует __name__, __doc__, __annotations__ и др.
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@good_decorator
def my_function():
    """Моя функция"""
    pass

print(my_function.__name__)  # 'my_function' (а не 'wrapper')
print(my_function.__doc__)   # 'Моя функция'

Декоратор для класса

from functools import wraps

def add_repr(cls):
    """Добавляет __repr__ к классу"""
    original_init = cls.__init__
    
    @wraps(original_init)
    def new_init(self, *args, **kwargs):
        original_init(self, *args, **kwargs)
    
    cls.__init__ = new_init
    
    def __repr__(self):
        attrs = ', '.join(f'{k}={v!r}' for k, v in self.__dict__.items())
        return f'{cls.__name__}({attrs})'
    
    cls.__repr__ = __repr__
    return cls

@add_repr
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

person = Person('Alice', 30)
print(person)  # Person(name='Alice', age=30)

Декоратор для методов

from functools import wraps

def require_login(func):
    """Проверяет, залогинен ли пользователь"""
    @wraps(func)
    def wrapper(self, *args, **kwargs):
        if not hasattr(self, 'user') or self.user is None:
            raise PermissionError('User not logged in')
        return func(self, *args, **kwargs)
    
    return wrapper

class API:
    def __init__(self):
        self.user = None
    
    @require_login
    def get_user_data(self):
        return {'id': 1, 'name': 'Alice'}

api = API()
# api.get_user_data()  # Raises PermissionError
api.user = 'alice'
print(api.get_user_data())  # OK

Краткая сравнение: Непараметрический vs Параметрический

# Непараметрический (просто)
@decorator
def func():
    pass

# Параметрический (сложнее)
@decorator(arg1, arg2)
def func():
    pass

Ключевые моменты

✅ Непараметрический декоратор состоит из двух функций: внешней и внутренней wrapper ✅ Всегда используй @functools.wraps для сохранения метаданных функции ✅ *args и **kwargs позволяют работать с функциями любых сигнатур ✅ Декоратор возвращает функцию wrapper, которая заменяет исходную функцию ✅ Можно применять к функциям и методам класса ✅ Декораторы можно комбинировать (использовать несколько вместе)