Шаг 167.
Python: сборник рецептов. Метапрограммирование. Использование декораторов для исправления определений классов

    На этом шаге мы рассмотрим решение этой задачи.

Задача

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

Решение

    Для этого очень удобно использовать декоратор класса. Например, вот декоратор класса, который изменяет специальный метод __getattrubute__, чтобы добавить логирование.

>>> def log_getattribute(cls):
    # Получение изначальной реализации
    orig_getattribute = cls.__getattribute__

    # Создание нового определения
    def new_getattribute(self, name):
        print('getting:', name)
        return orig_getattribute(self, name)

    # Прикрепление к классу и возврат
    cls.__getattribute__ = new_getattribute
    return cls

>>> # Пример использования
>>> @log_getattribute
class A:
    def __init__(self,x):
        self.x = x
    def spam(self):
        pass

>>> 

    Вот как это работает:

>>> a = A(42)
>>> a.x
getting: x
42
>>> a.spam()
getting: spam
>>> 


Обсуждение

    Декораторы классов часто используют как простую альтернативу более продвинутым приемам типа миксинов и метаклассов. Например, альтернативная реализация вышеприведенного решения может быть создана на базе наследования:

class LoggedGetattribute:
    def __getattribute__(self, name):
        print('getting:', name)
        return super().__getattribute__(name)

# Пример
class A(LoggedGetattribute):
    def __init__(self,x):
        self.x = x

    def spam(self):
        pass

    Это работает, но чтобы понять такой код, нужно уделить внимание порядку разрешения методов, функции super() и прочим аспектам наследования, описанным на 136 шаге. В каком-то смысле решение с использованием декоратора класса работает намного более прямолинейно и не вводит новых зависимостей в иерархию наследования. Как оказывается, такая реализация и работает немного быстрее, поскольку не полагается на функцию super().

    Если вы применяете несколько декораторов к классу, порядок применения может иметь значение. Например, декоратор, который заменяет метод полностью новой реализацией, вероятно, должен быть применен перед декоратором, который просто оборачивает существующий метод дополнительной логикой.

    См. 142 шаг, где приведен еще один пример использования декораторов классов.

    На следующем шаге мы рассмотрим использование метакласса для управления созданием экземпляров.




Предыдущий шаг Содержание