Классы-декораторы в Python

В этой статье вы узнаете о декораторах классов и научитесь определять классы в качестве декораторов в Python.

В предыдущих статьях мы обсуждали, как использовать функции для определения декораторов.

Например, следующая функция выводит количество символов * до и после вызова декорированной функции:

def star(n):
    def decorate(fn):
        def wrapper(*args, **kwargs):
            print(n*'*')
            result = fn(*args, **kwargs)
            print(result)
            print(n*'*')
            return result
        return wrapper
    return decorate

Функция star() — это так называемая фабрика декораторов, которая возвращает декоратор. Фабрика принимает аргумент, задающий количество * символов, которые нужно вывести.

Вот, как используется эта фабрика декораторов:

@star(5)
def add(a, b):
    return a + b


add(10, 20)

Вывод

*****
30
*****

Фабрика декоратора star() принимает аргумент и возвращает callable-объект. Callable-объект принимает аргумент — некую функцию fn(), которая будет декорирована. Также callable-объект может получить доступ к аргументу n, переданному фабрике декораторов.

Экземпляр класса может быть callable, если он реализует метод __call__. Поэтому метод __call__ можно сделать декоратором.

В следующем примере перепишем фабрика декораторов star с использованием класса:

 

class Star:
    def __init__(self, n):
        self.n = n

    def __call__(self, fn):
        def wrapper(*args, **kwargs):
            print(self.n*'*')
            result = fn(*args, **kwargs)
            print(result)
            print(self.n*'*')
            return result
        return wrapper

Класс Star в качестве декоратора можно использовать следующим образом:

@Star(5)
def add(a, b):
    return a + b

Функция @Star(5) возвращает экземпляр класса Star. Этот экземпляр является вызываемым, поэтому вы можете сделать что-то вроде такого:

add = Star(5)(add)

Соберем все вместе:

from functools import wraps


class Star:
    def __init__(self, n):
        self.n = n

    def __call__(self, fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            print(self.n*'*')
            result = fn(*args, **kwargs)
            print(result)
            print(self.n*'*')
            return result
        return wrapper


@Star(5)
def add(a, b):
    return a + b


add(10, 20)

Что нужно запомнить

  • Вызываемые классы можно использовать в качестве декораторов, реализовав метод __call__.
  • Аргументы декоратора передаются в метод __init__.
codechick

СodeСhick.io - простой и эффективный способ изучения программирования.

2024 ©