Это руководство предоставляет простые для понимания знания о декораторе Python с наглядными примерами.

В настоящее время Python стал одним из самых популярных языков программирования в мире. Он не только прост в освоении, но и предоставляет множество интересных и полезных функций. Декоратор (@), который позволяет нам изменять поведение других функций и классов, должен быть одним из них. Его основная цель - инкапсулировать сложные реализации, но при этом сделать ваш код ясным и красивым. Декоратор может немного сбить с толку новичков, но эта статья докажет, что он намного проще, чем вы когда-либо думали.

Синтаксис

@my_decorator
def hello_world():
    print('Hello world!')

В приведенном выше примере мы видим очень простую функцию, которая печатает строку «Hello world!». Но разница в том, что он определяется с помощью декоратора, который приводит к некоторым изменениям функции. Но если бы мы не знали @, как мы могли бы переписать приведенный выше код без использования этой функции? Вот ответ:

def hello_world():
    print('Hello world!')

hello_world = my_decorator(hello_world)

my_decorator - это вызываемая функция. Внутри этой функции есть другая функция, которая выполняет некоторые действия, включая вызов функции, переданной в качестве аргумента, которым в данном случае является hello_world. Если вам все еще сложно понять, переходим к следующей части статьи.

Создайте свой собственный декоратор

Теперь мы определяем упомянутую выше функцию my_decorator:

def my_decorator(func):
    def add_star():
        print('***')
        func()
        print('***')
    return add_star

@my_decorator
def hello_world():
    print('Hello world!')

hello_world()

При добавлении декоратора, как указано выше, hello_world передается вызываемому my_decorator. Внутри add_star определено действие для печати строк звездочки до и после вызова hello_world, который передается в качестве аргумента. Наконец, возвращается add_star и присваивается hello_world. С этого момента hello_world больше не является исходной функцией, а является новой. Ведь на выходе получится:

***
Hello world!
***

Функция декорирования содержит параметры

Приведенный выше пример представляет собой простой фрагмент кода, который просто выводит на консоль некоторую строку. Если определена функция, требующая некоторых параметров, чтобы принять их, мы просто добавляем *args и **kwargs при определении функции, возвращаемой декоратором.

def print_result_decorator(func):
    def print_result(*args, **kwargs):
        result = func(*args, **kwargs)
        print(f'Your result is {result}')
        return result
    return print_result

@print_result_decorator
def square(a):
    return a * a

square(2)

Выход:

Your result is 4

Класс как декоратор

В этой статье было показано несколько примеров декоратора функций, но мы можем использовать класс для обеспечения той же функциональности. Теперь я перепишу функцию print_result_decorator, используя стиль класса.

class ClassResultDecorator:
    def __init__(self, func):
        self.func = func
    def __call__(self, *args, **kwargs):
        result = self.func(*args, **kwargs)
        print(f'Your result is {result}')
        return result

@ClassResultDecorator
def square(a):
    return a * a

square(4)

Выход:

Your result is 16

При определении функции, как указано выше, сначала вызывается метод __init__ для инициализации экземпляра, где square передается и устанавливается как его свойство. Затем экземпляр возвращается и назначается square. При вызове square(4) это означает, что мы вызываем этот экземпляр ClassResultDecorator, а метод __call__ вызывается и выполняет его поведение.

Декоратор как обратный вызов

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

class Calculator:
    def connect(self, func):
        self.printer = func
    def sum(self, a, b):
        result = a + b
        if hasattr(self, 'printer') and self.printer is not None:
            self.printer(result)
        return result

cal = Calculator()

@cal.connect
def print_result(result):
    print(f'Your result is {result}')

cal.sum(1, 2)
cal.sum(10, 10)

Выход:

Your result is 3
Your result is 20

Чтобы передать функцию для обратного вызова, нам нужно определить функцию класса как декоратор, ее цель - сохранить переданную функцию как свойство экземпляра класса. В этой ситуации connect - это тот, который сохраняет print_result как свойство printer экземпляра калькулятора cal. Каждый раз, когда мы вызываем cal.sum(), внутри этого метода будет вызываться printer() и выводить итоговый результат на консоль.

Заключение

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

Помимо всего прочего, спасибо за то, что уделили время.