ПрограммированиеBackend разработчик

Что такое декораторы классов в Python? Как с их помощью можно модифицировать или дополнять функциональность классов? Приведите пример и расскажите о возможных нюансах.

Проходите собеседования с ИИ помощником Hintsage

Ответ.

Декоратор класса — это функция, принимающая класс в качестве аргумента и возвращающая либо модифицированный, либо совершенно новый класс. С их помощью можно динамически добавлять методы, изменять поведение существующих методов или даже возвращать подкласс, расширяющий функциональность оригинального класса.

Простейший пример декоратора класса:

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(3, 4) print(p) # <Point: {'x': 3, 'y': 4}>
  • Декоратор add_repr динамически добавляет метод __repr__ ко всем классам, к которым он применён.

Нюансы:

  • Декораторы классов могут возвращать не только вложения в класс, но и полностью обернутые (например, прокси-классы или наследники).
  • Ошибки можно получить, если декоратор не поддерживает аргументы *args и **kwargs, используемые при создании экземпляров класса.

Вопрос с подвохом.

Что произойдет, если применить два декоратора класса друг за другом? Всегда ли порядок их применения очевиден?

Ответ:

Декораторы применяются "снизу вверх": сначала применяется декоратор, находящийся непосредственно над объявлением класса, затем следующий выше и т.д. Порядок ОЧЕНЬ ВАЖЕН, так как результат первого декоратора передаётся во второй и так далее.

@dec1 @dec2 class Test: ... # Равносильно: # Test = dec1(dec2(Test))

Примеры реальных ошибок из-за незнания тонкостей темы.


История

В е-commerce приложении хотели логировать методы класса, но случайно случайно вернули не тот объект из декоратора, и методы потеряли свойство класса, из-за чего перестали правильно наследоваться, сломав работу модели.


История

На проекте для автогенерации новых методов использовали декоратор-добавлятор, но забыли про super() при возврате подкласса. В результате нарушилась иерархия и MRO, приведя к неработающим вызовам базового класса в дочерних.


История

В data pipeline попытались обернуть классы несколькими декораторами (логер, трекер изменений), но из-за неверного порядка применения получили конфликт имен методов, что привело к багам в продакшене из-за "потерянных" методов.