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.
Чтобы функция была декоратором, она должна соответствовать следующим свойствам.
- Он должен принимать функцию в качестве входных данных.
- Он должен содержать вложенную функцию.
- Он должен возвращать функцию.
Сначала мы определим функцию 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.