Декораторы в Python представляют функцию, которая в качестве параметра получает функцию и в качестве результата также возвращает функцию. Декораторы позволяют модифицировать выполняемую функцию, значения ее параметров и ее результат без изменения исходного кода этой функции.
Рассмотрим простейший пример:
# определение функции декоратора
def select(input_func):
def output_func(): # определяем функцию, которая будет выполняться вместо оригинальной
print("*****************") # перед выводом оригинальной функции выводим всякую звездочки
input_func() # вызов оригинальной функции
print("*****************") # после вывода оригинальной функции выводим всякую звездочки
return output_func # возвращаем новую функцию
# определение оригинальной функции
@select # применение декоратора select
def hello():
print("Hello METANIT.COM")
# вызов оригинальной функции
hello()
Вначале определяется собственно функция декоратора, которая в данном случае называется select(). В качестве параметра декоратор получает функцию (в данном случае параметр
input_func), к которой этот декоратор будет применяться:
def select(input_func):
def output_func(): # определяем функцию, которая будет выполняться вместо оригинальной
print("*****************") # перед выводом оригинальной функции выводим всякую звездочки
input_func() # вызов оригинальной функции
print("*****************") # после вывода оригинальной функции выводим всякую звездочки
return output_func # возвращаем новую функцию
Результатом декоратора в данном случае является локальная функция output_func, в которой вызывается входная функция input_func. Для простоты здесь перед и после
вызыва input_func для красоты просто выводим набор символов "*".
Далее определяется стандартная функция, к которой применяется декоратор - в данном случае это функция hello, которая просто выводит на консоль некоторую строку:
@select # применение декоратора select
def hello():
print("Hello METANIT.COM")
Для применения декоратора перед определением функции указывается символ @, после которого идет имя декоратора. То есть в данном случае к функции hello() применяется
декоратор select().
Далее вызываем обычную функцию:
hello()
Поскольку к этой функции применяется декоратор select, то в результате функциия hello передается в декоратор select() в качестве параметра input_func.
И поскольку декоратор возвращает новую функцию - output_func, то фактически в данном случае будет выполняться именно эта функция output_func()
В итоге мы получим следующий консольный вывод:
***************** Hello METANIT.COM *****************
Декоратор может перехватывать передаваемые в функцию аргументы:
# определение функции декоратора
def check(input_func):
def output_func(*args): # через *args получаем значения параметров оригинальной функции
input_func(*args) # вызов оригинальной функции
return output_func # возвращаем новую функцию
# определение оригинальной функции
@check
def print_person(name, age):
print(f"Name: {name} Age: {age}")
# вызов оригинальной функции
print_person("Tom", 38)
Здесь функция print_person() принимает два параметра: name (имя) и age (возраст). К этой функции применяется декоратор check()
В декораторе check возвращается локальная функция output_func(), которая принимает некоторый набор значений в виде параметра *args - это те
значения, которые передаются в оригинальную функцию, к которой применяется декоратор. То есть в данном случае *args будет содержать значения параметров name и age.
def check(input_func):
def output_func(*args): # через *args получаем значения параметров функции input_func
Здесь просто передаем эти значения в оригинальную функцию:
input_func(*args)
В итоге в данном получим следующий консольный вывод
Name: Tom Age: 38
Но что, если в функцию print_person будет передано какое-то недопустимое значение, например, отрицательный возраст? Одним из преимуществ декораторов как раз является то, что мы можем проверить и при необходимости модифицировать значения параметров. Например:
# определение функции декоратора
def check(input_func):
def output_func(*args):
name = args[0]
age = args[1] # получаем значение второго параметра
if age < 0: age = 1 # если возраст отрицательный, изменяем его значение на 1
input_func(name, age) # передаем функции значения для параметров
return output_func
# определение оригинальной функции
@check
def print_person(name, age):
print(f"Name: {name} Age: {age}")
# вызов оригинальной функции
print_person("Tom", 38)
print_person("Bob", -5)
args фактически представляет набор значений, и, используя индексы, мы можем получить значения параметров по позиции и что-то с ними сделать. Так, здесь, если значение возраста меньше 0, то устанавливаем 1. Затем передаем эти значения в вызов функции. В итоге здесь получим следующий вывод:
Name: Tom Age: 38 Name: Bob Age: 1
Подобным образом можно получить результат функции и при необходимости изменить его:
# определение функции декоратора
def check(input_func):
def output_func(*args):
result = input_func(*args) # передаем функции значения для параметров
if result < 0: result = 0 # если результат функции меньше нуля, то возвращаем 0
return result
return output_func
# определение оригинальной функции
@check
def sum(a, b):
return a + b
# вызов оригинальной функции
result1 = sum(10, 20)
print(result1) # 30
result2 = sum(10, -20)
print(result2) # 0
Здесь определена функция sum(), которая возвращает сумму чисел. В декораторе check проверяем результат функции и для простоты, если он меньше нуля, то возвращаем 0.
Консольный вывод программы:
30 0