Что такое декораторы
Декоратор — это функция, которая принимает другую функцию (или класс) в качестве аргумента и возвращает новую функцию (или класс) с расширенной функциональностью, сохраняя имя исходного объекта. В Python декораторы — это синтаксический сахар, который позволяет модифицировать поведение функций, методов и классов без изменения их исходного кода.
Как это работает
В Python функции — это объекты первого класса, поэтому их можно передавать в другие функции и возвращать из них. Декоратор использует это свойство: он принимает функцию func, определяет внутри новую функцию-обёртку wrapper, которая добавляет логику до и после вызова func, и возвращает wrapper. Синтаксис @decorator перед определением функции эквивалентен присваиванию func = decorator(func).
Пример простого декоратора:
import functools
def log_decorator(func):
@functools.wraps(func) # сохраняет имя и docstring исходной функции
def wrapper(*args, **kwargs):
print(f"Вызов {func.__name__}")
result = func(*args, **kwargs)
print(f"Результат: {result}")
return result
return wrapper
@log_decorator
def add(a, b):
"""Складывает два числа"""
return a + b
add(2, 3) # Вызов add, Результат: 5Здесь @log_decorator заменяет add на wrapper, который логирует вызов и результат.
Декораторы классов
Декораторы могут применяться не только к функциям, но и к классам. В этом случае декоратор принимает класс и возвращает новый класс (или модифицирует исходный). Это удобно для добавления методов, атрибутов или изменения поведения экземпляров.
Пример декоратора класса:
def add_repr(cls):
def __repr__(self):
return f"{cls.__name__}({self.__dict__})"
cls.__repr__ = __repr__
return cls
@add_repr
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(1, 2)
print(p) # Point({'x': 1, 'y': 2})Декораторы классов часто используют как альтернативу наследованию, когда нужно добавить функциональность нескольким несвязанным классам.
Паттерн «Декоратор»
В объектно-ориентированном программировании паттерн «Декоратор» позволяет динамически добавлять объектам новые обязанности, оборачивая их в классы-обёртки. В Python этот паттерн реализуется через композицию: класс-декоратор хранит ссылку на оборачиваемый объект и делегирует ему вызовы, добавляя своё поведение.
Пример из исходного ответа:
class TextTag:
"""Базовый класс для текстового тега"""
def __init__(self, text):
self._text = text
def render(self):
return self._text
class BoldWrapper(TextTag):
"""Оборачивает текст в <b>"""
def __init__(self, wrapped):
self._wrapped = wrapped
def render(self):
return f"<b>{self._wrapped.render()}</b>"
class ItalicWrapper(TextTag):
"""Оборачивает текст в <i>"""
def __init__(self, wrapped):
self._wrapped = wrapped
def render(self):
return f"<i>{self._wrapped.render()}</i>"
simple_hello = TextTag("hello, world!")
special_hello = ItalicWrapper(BoldWrapper(simple_hello))
print(simple_hello.render()) # hello, world!
print(special_hello.render()) # <i><b>hello, world!</b></i>Здесь BoldWrapper и ItalicWrapper — декораторы, которые добавляют HTML-теги к тексту, не изменяя базовый класс.
Подводные камни
- При использовании декораторов без
functools.wrapsтеряются метаданные исходной функции (__name__,__doc__), что затрудняет отладку. - Декораторы выполняются при определении функции, а не при её вызове. Это важно учитывать при использовании внешних ресурсов.
- Если декоратор принимает аргументы, нужен дополнительный уровень вложенности:
@decorator(arg)возвращает декоратор, который принимает функцию. - Декораторы классов могут изменять класс неожиданным образом, если неаккуратно модифицировать его атрибуты.
Коротко
- Декоратор — функция, принимающая функцию или класс и возвращающая расширенную версию.
- Синтаксис
@decorator— это сахар дляfunc = decorator(func). - Декораторы бывают для функций, методов и классов.
- Паттерн «Декоратор» реализуется через композицию и позволяет добавлять поведение без наследования.
- Используйте
functools.wraps, чтобы сохранять метаданные исходной функции.
