Рубрики
Без рубрики

Введение в Python Декораторы

Автор оригинала: Nicholas Samuel.

Введение в Python Декораторы

Вступление

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

В этой статье мы подробно обсудим декораторы Python.

Как создать декораторов

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

def lowercase(func):
    def wrapper():
        func_ret = func()
        change_to_lowercase = func_ret.lower()
        return change_to_lowercase

    return wrapper

В приведенном выше сценарии мы просто создали декоратор с именем lowercase , который принимает функцию в качестве аргумента. Чтобы опробовать нашу функцию lowercase , нам нужно создать новую функцию, а затем передать ее этому декоратору. Обратите внимание, что поскольку функции являются первоклассными в Python, вы можете назначить функцию переменной или рассматривать ее как таковую. Мы будем использовать этот трюк для вызова функции декоратора:

def hello_function():
    return 'HELLO WORLD'

decorate = lowercase(hello_function)
print(decorate())

Выход

hello world

Обратите внимание, что вы можете объединить вышеприведенные два фрагмента кода в один. Мы создали функцию hello_function () , которая возвращает предложение “HELLO WORLD”. Затем мы вызвали декоратор и передали имя этой функции в качестве аргумента, назначая его переменной “decorate”. При выполнении вы можете увидеть, что полученное предложение было преобразовано в нижний регистр.

Однако есть более простой способ применения декораторов в Python. Мы можем просто добавить символ @ перед именем функции декоратора чуть выше функции, которую нужно украсить. Например:

@lowercase
def hello_function():
    return 'HELLO WORLD'

print(hello_function())

Выход

hello world

Как применить несколько декораторов к функции

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

def split_sentence(func):
    def wrapper():
        func_ret = func()
        output = func_ret.split()
        return output

    return wrapper

Здесь мы создали декоратор, который берет входное предложение и разбивает его на различные части. Декоратору было дано имя split_sentence . Теперь давайте применим строчные и split_sentence декораторы к одной функции.

Чтобы выполнить эти операции в правильном порядке, примените их следующим образом:

@split_sentence
@lowercase
def hello_function():
    return 'HELLO WORLD'
print(hello_function())

Выход

['hello', 'world']

Наше предложение было разделено на два и преобразовано в нижний регистр, так как мы применили оба lowercase и split_sentence декораторы к hello_function .

Передача аргументов функциям декоратора

Декораторы Python также могут перехватывать аргументы, передаваемые оформленным функциям. Аргументы, в свою очередь, будут переданы оформленной функции во время выполнения. Рассмотрим следующий пример:

def my_decorator(func):
    def my_wrapper(argument1, argument2):
        print("The arguments are: {0}, {1}".format(argument1, argument2))
        func(argument1, argument2)
    return my_wrapper


@my_decorator
def names(firstName, secondName):
    print("Your first and second names are {0} and {1} respectively".format(firstName, secondName))

print(names("Nicholas", "Samuel"))

Выход

The arguments are: Nicholas, Samuel
Your first and second names are Nicholas and Samuel respectively

В приведенном выше сценарии декоратор принимает два аргумента:, аргумент 1 и аргумент 1 .

Создание Декораторов Общего Назначения

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

Мы можем определить их с помощью аргументов args и **kwargs . Все позиционные и ключевые аргументы хранятся в этих двух переменных соответственно. С помощью args и kwargs мы можем передать любое количество аргументов во время вызова функции. Например:

def my_decorator(func):
    def my_wrapper(*args, **kwargs):
        print('Positional arguments:', args)
        print('Keyword arguments:', kwargs)
        func(*args)
    return my_wrapper

@my_decorator
def function_without_arguments():
    print("No arguments")

function_without_arguments()

Выход

Positional arguments: ()
Keyword arguments: {}
No arguments

Как видите, никаких аргументов декоратору передано не было.

Теперь давайте посмотрим, как мы можем передавать значения позиционным аргументам:

@my_decorator
def function_with_arguments(x, y, z):
    print(x, y, z)

function_with_arguments(5, 15, 25)

Выход

Positional arguments: (5, 15, 25)
Keyword arguments: {}
5 15 25

Мы передали декоратору три позиционных аргумента. Чтобы передать аргументы ключевых слов, мы должны использовать ключевые слова в вызове функции. Вот пример:

@my_decorator
def passing_keyword_arguments():
    print("Passing keyword arguments")

passing_keyword_arguments(firstName="Nicholas", secondName="Samuel")

Выход

Positional arguments: ()
Keyword arguments: {'secondName': 'Samuel', 'firstName': 'Nicholas'}
Passing keyword arguments

Два ключевых аргумента были переданы декоратору.

В следующем разделе мы обсудим, как отлаживать декораторы.

Как отлаживать декораторы

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

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

function_with_arguments.__name__

Выход

'my_wrapper'

Это представляет большую проблему во время отладки. Однако Python предоставляет functools.wraps декоратор, который может помочь в решении этой задачи. Он работает, копируя потерянные метаданные в ваше оформленное закрытие.

Теперь давайте продемонстрируем, как это работает:

import functools

def lowercase(func):
    @functools.wraps(func)
    def my_wrapper():
        return func().lower()
    return my_wrapper
@lowercase
def hello_function():
    "Saying hello"
    return 'HELLO WORLD'

print(hello_function())

Выход

hello world

Так как мы использовали functools.wraps в функции-оболочке мы можем проверить метаданные функции на наличие “hello_function”:

hello_function.__name__

Выход

'hello_function'
hello_function.__doc__

Выход

'Saying hello'

Приведенный выше сценарий ясно показывает, что метаданные теперь ссылаются на функцию, а не на оболочку. Я рекомендую вам всегда использовать functools.wraps в любое время, когда вы определяете декоратора. Это значительно облегчит вам отладку.

Вывод

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