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

Введение В 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() которая возвращает предложение «ПРИВЕТ, МИР». Затем мы вызвали декоратор и передали имя этой функции в качестве аргумента, присвоив его переменной 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 . Теперь применим lowercase и 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 

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

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

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

Мы можем определить их, используя 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

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus