2.23. Декораторы

Декоратор – это синтаксическая конструкция, которая оборачивает функцию (или метод) в другую функцию. Обёртка может добавлять поведение до и после исходного вызова, заменять возвращаемое значение или присоединять метаданные. Форма такова:

@wrapper
def f():
    ...

Строка @wrapper эквивалентна f = wrapper(f) – функция f создаётся как обычно, затем передаётся в wrapper, а результат повторно связывается с именем f.

Функция входит в "обёрточный" блок слева и выходит справа как декорированная функция.

Декоратор принимает функцию и возвращает новую функцию.

2.23.1. Встроенные декораторы методов

Несколько декораторов поставляются с Python и используются внутри тел классов.

2.23.1.1. @property

Превращает метод в вычисляемый атрибут. Вызывающий обращается к нему так, будто это обычный атрибут (без скобок), но при каждом чтении выполняется метод:

class Circle:
    def __init__(self, radius):
        self.radius = radius

    @property
    def area(self):
        return 3.14159 * self.radius * self.radius

c = Circle(5)
print(c.area)             # 78.53975  -- no parentheses

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

2.23.1.2. @classmethod

Определяет метод, который получает в качестве первого аргумента класс, а не экземпляр. Первый параметр по соглашению называется cls:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    @classmethod
    def origin(cls):
        return cls(0, 0)

p = Point.origin()

Методы класса – стандартный способ предоставления альтернативных конструкторов – фабричных функций, возвращающих экземпляр, построенный нестандартным образом.

2.23.1.3. @staticmethod

Определяет метод, который не получает ни экземпляр, ни класс – это просто обычная функция, которая живёт в пространстве имён класса по организационным причинам:

class Temperature:
    @staticmethod
    def c_to_f(c):
        return c * 9 / 5 + 32

Temperature.c_to_f(100)   # 212.0

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

2.23.2. Написание собственного декоратора

Декоратор – это функция, которая принимает функцию и возвращает функцию. Минимальная форма:

def log_calls(func):
    def wrapper(*args, **kwargs):
        print("calling", func.__name__)
        return func(*args, **kwargs)
    return wrapper

@log_calls
def add(a, b):
    return a + b

add(2, 3)

Вывод:

calling add

wrapper замыкается над func и передаёт ему всё. *args / **kwargs позволяют ему работать с любой функцией, а не только с двухаргументными. Этот шаблон является основой для более сложных декораторов (замер времени, кэширование, повтор при сбое), но суть всегда одна и та же: принять функцию, вернуть функцию.