Декораторы

Последнее обновление: 08.10.2022

Декораторы в 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
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850