Декоратор — это шаблон проектирования, который мы можем использовать для добавления новых функциональных возможностей к уже существующей функции без необходимости изменять ее структуру. Декоратор должен вызываться непосредственно перед функцией, которая должна быть расширена. С помощью декораторов вы можете динамически изменять функциональные возможности методов, функции или класса без непосредственного использования наследования. Это хороший, способ расширения функциональности функции, которую не хотите изменить напрямую.
В этой статье мы подробно обсудим декораторы в Python.
Давайте посмотрим, как декораторы могут быть созданы. В качестве примера мы создадим декоратор, который будет преобразовывать строки в нижний регистр. Для этого нам нужно создать функцию декоратора, и в ней нужно определить функцию оболочку, в нашем случае wrapper. Посмотрите на следующий код:
def lowercase(func): def wrapper(): func_ret = func() change_to_lowercase = func_ret.lower() return change_to_lowercase return wrapper
В приведенном выше сценарии мы создали декоратор с именем lowercase, который принимает функцию в качестве аргумента. Чтобы опробовать нашу функцию lowercase, нам нужно создать новую функцию и затем передать ее этому декоратору. Обратите внимание, что поскольку функции в Python являются first-class, вы можете назначить функцию переменной или рассматривать ее как единое целое. Мы будем использовать этот трюк для вызова функции декоратора:
def hello_function(): return 'HELLO WORLD' decorate = lowercase(hello_function) print(decorate())
Output
hello world
Обратите внимание, что вы можете объединить вышеупомянутые две части кода в один. Мы создали функцию hello_function(), которая возвращает предложение «HELLO WORLD». Затем мы вызвали декоратор и передали имя этой функции в качестве аргумента, присваивая его переменной decorate. После выполнения можно увидеть, что полученное предложение было преобразовано в нижний регистр.
Тем не менее, есть более простой способ применения декораторов в Python. Можно просто добавить символ @ перед именем функции декоратора чуть выше декорируемой функции. Например:
@lowercase def hello_function(): return 'HELLO WORLD' print(hello_function())
Output
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())
Output
['hello', 'world']
Наше предложение было разделено на две части и преобразовано в строчные буквы, так как мы применили к hello_function декораторы lowercase и split_sentence.
Декораторы 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"))
Output
The arguments are: Nicholas, Samuel Your first and second names are Nicholas and Samuel respectively
В приведенном выше сценарии декоратор принимает два аргумента :, argument1 и argument2.
Декораторы общего назначения могут быть применены к любой функции. Такого рода декораторы очень полезны, например, для отладки.
Мы можем определить их, используя аргументы *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()
Output
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)
Output
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")
Output
Positional arguments: () Keyword arguments: {'secondName': 'Samuel', 'firstName': 'Nicholas'} Passing keyword arguments
Два именных аргумента были переданы декоратору.
В этот момент вы, наверное, видели, что мы используем декораторы для обертывания вокруг функции. Обертывание функции это по сути замыкание. Замыкание скрывает исходное имя функции, список ее параметров и строку документации docstring.
Например: если мы попытаемся получить метаданные для декоратора function_with_arguments, мы получим метаданные замыкания (то есть функции обертки). Давайте продемонстрируем это:
function_with_arguments.__name__
Output
'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())
Output
hello world
Поскольку мы использовали functools.wraps в функции-обертке, мы можем проверить метаданные функции на «hello_function»:
hello_function.__name__
Output
'hello_function'
hello_function.__doc__
Output
'Saying hello'
Приведенный выше скрипт ясно показывает, что метаданные теперь ссылаются на функцию, а не на оболочку. Я рекомендую вам всегда использовать functools.wraps каждый раз, когда вы определяете декоратор. Это намного упростит отладку, в случае необходимости.
Цель декораторов — динамически изменять функциональные возможности класса, метода или функции без непосредственного использования наследования или изменения исходного кода класса, метода или функции, которую нам нужно декорировать. В этой статье мы увидели, как создавать простые и универсальные декораторы и как передавать аргументы декораторам. Мы также увидели, как отлаживать декораторы во время разработки с помощью модуля functools.
Оригинал: Introduction to Python Decorators
Краткий перевод: https://vuejs.org/guide/components/v-model.html Основное использование v-model используется для реализации двусторонней привязки в компоненте. Начиная с Vue…
Сегодня мы рады объявить о выпуске Vue 3.4 «🏀 Slam Dunk»! Этот выпуск включает в…
Vue.js — это универсальный и адаптируемый фреймворк. Благодаря своей отличительной архитектуре и системе реактивности Vue…
Недавно, у меня истек сертификат и пришлось заказывать новый и затем устанавливать на хостинг с…
Каким бы ни было ваше мнение о JavaScript, но всем известно, что работа с датами…
Все, кто следит за последними событиями в мире адаптивного дизайна, согласятся, что введение контейнерных запросов…