Декораторы в Python

721
Декораторы в Python
Декораторы в Python

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

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

Что такое декораторы в Python?

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

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

Концепции, необходимые для понимания декораторов

Объекты первого класса

В Python объекты первого класса – это те объекты, которые

  • могут быть переданы в функцию в качестве параметра.
  • могут быть возвращены из функции.
  • могут быть присвоены переменной.

Все переменные, которые мы используем в наших программах, являются объектами первого класса, будь то примитивный тип данных, объект коллекции или объекты, определенные с помощью классов.

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

Для примера рассмотрим следующий исходный код.

Здесь мы определили функцию add(), которая принимает на вход два числа и выводит их сумму. Мы определили другую функцию random_adder(), которая принимает функцию в качестве входных данных, случайным образом генерирует два числа и вызывает входную функцию add() со случайно сгенерированными числами в качестве входных данных.

import random


def add(num1, num2):
   value = num1 + num2
   print("In the add() function. The sum of {} and {} is {}.".format(num1, num2, value))


def random_adder(func):
   val1 = random.randint(0, 10)
   val2 = random.randint(0, 100)
   print("In the random_adder. Values generated are {} and {}".format(val1, val2))
   func(val1, val2)


# execute
random_adder(add)

Вывод:

In the random_adder. Values generated are 1 and 14
In the add() function. The sum of 1 and 14 is 15.

Из кода и вывода видно, что функция add() была передана функции random_adder() в качестве входной, а функция random_adder() вызывает функцию add(), которая печатает вывод.

Мы также можем вернуть функцию из другой функции или вызываемого объекта. Например, мы можем изменить приведенный выше исходный код и определить функцию operate() внутри функции random_adder(). Функция operate() выполняет всю операцию, выполненную функцией random_adder() в предыдущем исходном коде.

Теперь мы можем вернуть функцию operate() из функции random_adder() и присвоить ее переменной do_something. Таким образом, мы сможем выполнить функцию operate() вне функции random_adder(), вызвав переменную do_something следующим образом.

import random


def add(num1, num2):
   value = num1 + num2
   print("In the add() function. The sum of {} and {} is {}.".format(num1, num2, value))


def random_adder(func):
   print("In the random_adder.")

   def operate():
       val1 = random.randint(0, 10)
       val2 = random.randint(0, 100)
       print("In the operate() function. Values generated are {} and {}".format(val1, val2))
       func(val1, val2)

   print("Returning the operate() function.")
   return operate


# execute
do_something = random_adder(add)
do_something()

Вывод:

In the random_adder.
Returning the operate() function.
In the operate() function. Values generated are 3 and 25
In the add() function. The sum of 3 and 25 is 28.

Вложенные функции

Вложенные функции – это функции, определенные внутри другой функции. Например, посмотрите на следующий исходный код.

Здесь мы определили функцию add(), которая принимает на вход два числа и вычисляет их сумму. Также мы определили функцию square() внутри add(), которая печатает квадрат “value”, вычисленного в функции add().

def add(num1, num2):
   value = num1 + num2
   print("In the add() function. The sum of {} and {} is {}.".format(num1, num2, value))

   def square():
       print("I am in square(). THe square of {} is {}.".format(value, value ** 2))

   print("calling square() function inside add().")
   square()


# execute
add(10, 20)

Вывод:

In the add() function. The sum of 10 and 20 is 30.
calling square() function inside add().
I am in square(). THe square of 30 is 900.

Свободные переменные

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

В приведенном выше примере видно, что мы определили переменную “value” внутри функции add(), но обратились к ней в функции square(). Такие типы переменных называются свободными переменными.

Но почему называется свободными переменными?

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

def add(num1, num2):
   value = num1 + num2
   print("In the add() function. The sum of {} and {} is {}.".format(num1, num2, value))

   def square():
       print("I am in square(). THe square of {} is {}.".format(value, value ** 2))

   print("returning square() function.")
   return square


# execute
do_something = add(10, 20)
print("In the outer scope. Calling do_something.")
do_something()

Вывод:

In the add() function. The sum of 10 and 20 is 30.
returning square() function.
In the outer scope. Calling do_something.
I am in square(). THe square of 30 is 900.

Здесь, как только функция add() возвращает функцию square(), она завершает свое выполнение и очищается из памяти. Тем не менее, мы можем получить доступ к переменной “value“, вызвав функцию square(), которая была присвоена переменной do_something.

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

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

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

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

  1. Он должен принимать функцию в качестве входных данных.
  2. Он должен содержать вложенную функцию.
  3. Он должен возвращать функцию.

Сначала мы определим функцию add(), которая принимает на вход два числа и выводит их сумму.

def add(num1, num2):
   value = num1 + num2
   print("The sum of {} and {} is {}.".format(num1, num2, value))


# execute
add(10, 20)

Вывод:

The sum of 10 and 20 is 30.

Теперь нам нужно определить декоратор таким образом, чтобы функция add() также выводила произведение чисел вместе с суммой. Для этого мы можем создать функцию-декоратор.

Сначала определим функцию, которая принимает функцию add() в качестве входных данных и наделяет ее дополнительными требованиями.

def decorator_function(func):
   def inner_function(*args):
       product = args[0] * args[1]
       print("Product of {} and {} is {} ".format(args[0], args[1], product))
       func(args[0], args[1])

   return inner_function

Внутри функции decorator_function() мы определили функцию inner_function(), которая выдает произведение чисел, заданных в качестве входных данных, а затем вызывает функцию add(). Функция decorator_function() возвращает функцию inner_function().

Теперь, когда мы определили функцию decorator_function() и функцию add(), давайте посмотрим, как мы можем использовать функцию add() с помощью функции decorator_function().

Создавайте декораторы в Python, передавая функции в качестве аргументов другой функции

Первый способ декорирования функции add() – это передача ее в качестве входного аргумента в функцию decorator_function(). После вызова функции decorator_function() она вернет inner_function(), которая будет присвоена переменной do_something. После этого переменная do_something станет вызываемой и при вызове будет выполнять код внутри inner_function(). Таким образом, мы можем вызвать do_something для печати произведения и суммы введенных чисел.

def add(num1, num2):
   value = num1 + num2
   print("The sum of {} and {} is {}.".format(num1, num2, value))


def decorator_function(func):
   def inner_function(*args):
       product = args[0] * args[1]
       print("Product of {} and {} is {} ".format(args[0], args[1], product))
       func(args[0], args[1])

   return inner_function


# execute
do_something = decorator_function(add)
do_something(10, 20)

Вывод:

Product of 10 and 20 is 200
The sum of 10 and 20 is 30.

Создание декораторов в Python с помощью @

Более простой способ выполнить ту же операцию – использовать знак “@“. Мы можем указать имя функции decorator_function после знака @ перед определением функции add(). После этого при каждом вызове функции add() она всегда будет выводить и произведение, и сумму введенных чисел.

def decorator_function(func):
   def inner_function(*args):
       product = args[0] * args[1]
       print("Product of {} and {} is {} ".format(args[0], args[1], product))
       return func(args[0], args[1])
   return inner_function


@decorator_function
def add(num1, num2):
   value = num1 + num2
   print("The sum of {} and {} is {}.".format(num1, num2, value))


# execute
add(10, 20)

Вывод:

Product of 10 and 20 is 200
The sum of 10 and 20 is 30.

В этом методе есть недостаток – вы не можете использовать функцию add() для простого сложения чисел. Она всегда будет выводить произведение чисел вместе с их суммой. Итак, выбирайте методику, с помощью которой вы собираетесь реализовать декораторы, правильно проанализировав свои задачи.

Заключение

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