Декораторы в Python: расширяем функциональность без изменения кода
Узнайте, как использовать декораторы в Python для элегантного расширения функциональности. Разбираем практические примеры: логирование, кэширование, измерение времени выполнения и проверку прав доступа. Понятные объяснения с примерами кода для начинающих и опытных разработчиков.
Представьте, что вы разрабатываете веб-приложение и вам нужно логировать каждый вызов API, проверять права доступа пользователей и измерять время выполнения функций. Можно, конечно, копировать один и тот же код в каждую функцию, но есть способ элегантнее — декораторы. Это один из самых мощных инструментов Python, который позволяет модифицировать поведение функций без изменения их исходного кода.
Что такое декораторы
Декоратор — это функция, которая принимает другую функцию в качестве аргумента и возвращает новую функцию с расширенной функциональностью. Звучит сложно? На самом деле всё проще, чем кажется.
def my_decorator(func):
def wrapper():
print("Что-то происходит до вызова функции")
func()
print("Что-то происходит после вызова функции")
return wrapper
@my_decorator
def say_hello():
print("Привет!")
say_hello()Результат выполнения:
Что-то происходит до вызова функции
Привет!
Что-то происходит после вызова функцииСимвол @ — это синтаксический сахар Python. Запись @my_decorator перед функцией эквивалентна say_hello = my_decorator(say_hello).
Декораторы с аргументами
Но что делать, если наша функция принимает параметры? Для этого используем *args и **kwargs:
def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(times=3)
def greet(name):
print(f"Привет, {name}!")
greet("Алексей")Вывод:
Привет, Алексей!
Привет, Алексей!
Привет, Алексей!Здесь мы создали декоратор с параметром, который повторяет вызов функции заданное количество раз.

Практические примеры декораторов
Измерение времени выполнения
import time
from functools import wraps
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"Функция {func.__name__} выполнялась {end - start:.4f} секунд")
return result
return wrapper
@timer
def heavy_computation():
time.sleep(2)
return "Готово"
heavy_computation()Обратите внимание на @wraps(func) — это встроенный декоратор из модуля functools, который сохраняет метаданные оригинальной функции (имя, документацию и так далее).
Кэширование результатов
def memoize(func):
cache = {}
@wraps(func)
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper
@memoize
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(100)) # Выполнится мгновенно благодаря кэшированиюПроверка прав доступа
def require_auth(func):
@wraps(func)
def wrapper(user, *args, **kwargs):
if not user.get('is_authenticated'):
raise PermissionError("Требуется авторизация")
return func(user, *args, **kwargs)
return wrapper
@require_auth
def delete_account(user, account_id):
print(f"Аккаунт {account_id} удалён")
user = {'is_authenticated': True, 'username': 'admin'}
delete_account(user, 123)Цепочки декораторов
Декораторы можно комбинировать, применяя несколько к одной функции:
@timer
@memoize
def complex_calculation(x, y):
time.sleep(1)
return x ** y
# Первый вызов займёт ~1 секунду
complex_calculation(2, 10)
# Второй вызов будет мгновенным (результат закэширован)
complex_calculation(2, 10)Декораторы применяются снизу вверх, то есть сначала memoize, затем timer.
Классы как декораторы
Декораторы не обязательно должны быть функциями. Можно использовать классы:
class CountCalls:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"Вызов #{self.count} функции {self.func.__name__}")
return self.func(*args, **kwargs)
@CountCalls
def process_data():
print("Обработка данных...")
process_data()
process_data()
process_data()
Встроенные декораторы Python
Python предоставляет несколько полезных встроенных декораторов:
@property— превращает метод в атрибут класса@staticmethod— создаёт статический метод@classmethod— создаёт метод класса@functools.lru_cache— кэширование с ограничением размера@dataclass— автоматическое создание методов класса
from functools import lru_cache
@lru_cache(maxsize=128)
def expensive_function(param):
# Сложные вычисления
return param * 2Когда использовать декораторы
Декораторы идеально подходят для задач, которые нужно применять к множеству функций: логирование, валидация входных данных, обработка исключений, контроль доступа, кэширование, измерение производительности, retry-логика при ошибках сети. Они помогают соблюдать принцип DRY (Don't Repeat Yourself) и делают код чище и понятнее.
Потенциальные проблемы
Несмотря на всю мощь декораторов, нужно помнить о нескольких моментах. Излишнее использование декораторов может усложнить отладку кода. Декораторы добавляют небольшие накладные расходы на производительность. Важно всегда использовать @wraps для сохранения метаданных функции. Цепочки из множества декораторов могут снижать читаемость кода.
Резюме
Декораторы — это элегантный способ модифицировать и расширять функциональность кода без его изменения. Они широко используются в популярных фреймворках вроде Flask и Django, и понимание их работы значительно упростит вашу жизнь как разработчика. Начните с простых декораторов для логирования или измерения времени, и постепенно вы обнаружите всё больше ситуаций, где они будут полезны.
Хотите освоить декораторы и другие продвинутые возможности Python на практике?
Приложение Кодик создано специально для того, чтобы вы могли учиться программированию в комфортном темпе, с понятными объяснениями на русском языке и реальными примерами из практики. Мы разбираем сложные темы простым языком — от основ до профессиональных техник. Каждый урок построен так, чтобы вы не просто запоминали синтаксис, а понимали, как применять знания в реальных проектах.
А если у вас возникнут вопросы в процессе обучения — добро пожаловать в наш Telegram-канал! Это живое сообщество разработчиков, где можно задать любой вопрос и получить развёрнутый ответ. Атмосфера у нас дружная и познавательная — каждый день мы разбираем топовые темы в разработке, делимся опытом и помогаем друг другу расти. Присоединяйтесь к Кодику — начните свой путь в программировании с теми, кто действительно понимает, как сделать обучение эффективным и интересным!