Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Непараметрический декоратор в 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, которая заменяет исходную функцию
✅ Можно применять к функциям и методам класса
✅ Декораторы можно комбинировать (использовать несколько вместе)