Сегодня мы поговорим о такой особенности языка Python как декораторы. Декораторы – это специально созданные функции, которые помогают добавить дополнительную функциональность в уже существующий код. Интересен тот факт, что в других языках (например, в С) подобных штук нет.
Декоратор меняет поведение других функций. При этом он не прерывает работу основной функции. Декораторы в Python могут быть как функциями, так и классами. Обычно декораторы вызываются перед определением функции, которую нужно декорировать.
Ниже давайте рассмотрим различные примеры использования декораторов.
Декораторы в Python: примеры использования
Пример 1
Давайте начнем разбирать действие декораторов на примерах. Возьмём декоратор с именем decorator1 и внутренний класс new_func. Обе эти функции содержат аргументы. Далее мы подробно рассмотрим функциональность декоратора с аргументами.
В этом примере мы добавляем символ доллара с номером, который указываем в конце вызова функции. Это вызов декорированной функции. myfunction используется, чтобы просто вернуть полученный аргумент. Исходный код приведен ниже. Выходные данные показывают, что символ $ и число объединены.

Пример 2
Разберем следующий пример. Есть два декоратора. Сначала определим decorator1, а затем wrapper как его внутреннюю функцию (англ. wrapper — обертка).
Во wrapper у нас сначала идет вывод первого сообщения (first message), затем функция, которая собственно и оборачивается в обертку при помощи декоратора, а затем — вывод второго сообщения (second message).
В конце функции decorator1 возвращается внутренняя функция wrapper.
Далее определим второй декоратор — decorator2. Эта функция просто выводит третье сообщение (3rd message).
После этого «декорируем» decorator2, как показано в предпоследней строке кода. И, наконец, последней строчкой кода мы вызываем декорированную функцию.
def decorator1(function):
def wrapper():
print('this is first message')
function()
print ('this is second message')
return wrapper
def decorator2():
print('this is 3rd message')
decorator2 = decorator1(decorator2)
decorator2()
В выводе сначала отображается первое сообщение. После этого выводится третье сообщение (вследствие вызова функции decorator2). А второе сообщение отображается лишь в конце.
this is first message this is 3rd message this is second message
Возврат значений из декорированных функций
Следующий пример касается передачи или получения аргументов в декораторе. Это чем-то похоже на передачу значений в обычные функции.
У внутренней функции в нашем примере есть параметры. При передаче аргументов для возврата значения с этим трудно справиться. Чтобы свести к минимуму эту проблему, в функции-оболочке мы будем использовать *args и **kwargs.
Ниже мы видим результат — python печатается первым, а Сoding is easy печатается позже. Это из-за порядка вызова функций с аргументами.

Создание цепочки декораторов
Мы составили цепочку декораторов с помощью символов звездочки и плюса. В данном примере для декорирования функции используется более одного декоратора.
Сначала определяем оба декоратора, со звездочками и плюсами. Затем оба декоратора прикрепляются к функции function(): они указываются над функцией, а перед ними ставится знак @. Таким образом функция модифицируется, а вывод будет декорированным.
def decorator1(f):
def wrapped():
return '*****' + f() + '*****'
return wrapped
def decorator2(f):
def wrapped():
return '+++' + f() + '+++'
return wrapped
@decorator1
@decorator2
def function():
return 'Python 3.8'
print(function())
В обертках звездочки и плюсы добавлены до и после вызова функции. В результате с каждой стороны строки прикрепляется по 5 звездочек и 3 знака «плюс».
*****+++Python 3.8+++*****
Добавление нескольких декораторов к одной функции
Пропишем два декоратора. Первый — для разделения строки. Второй — для приведения к верхнему регистру. После этого, под вызовами, мы определим еще один декоратор, который разделит предложение и сделает его списком.
def decorator1(function):
def wrapper():
func = function()
splitted_string = func.split()
return splitted_string
return wrapper
def decorator2uppercase(function):
def wrapper():
func = function()
make_uppercase = func.upper()
return make_uppercase
return wrapper
@decorator1
@decorator2uppercase
def say_hi():
return 'python is good language'
print(say_hi())
В приведенном выше коде сначала все буквы предложения переводятся в верхний регистр, а затем предложение разбивается на части. Получившиеся слова возвращаются в виде списка. Обратите внимание, что изначально предложение было написано строчными буквами.
['PYTHON', 'IS', 'GOOD', 'LANGUAGE']
Использование декораторов Python при обработке исключений
Теперь давайте обработаем исключение, используя декоратор. В качестве примера взят массив. После определения декораторов мы использовали функцию, которая принимает новое значение или позицию массива. Конкретное назначение этой функции – проверить условие if pos >= len(array): . Мы использовали здесь оператор if, чтобы упростить задачу.
Эта строка является основой всей программы, так как проверяет ее работоспособность. Она проверяет, не превышает ли позиция массива размер массива, и в случае превышения выводит сообщение об ошибке. В противном случае функция будет выполнять действие декораторов.
В примере на скриншоте значение индекса больше размера массива. Следовательно, в выводе мы увидим сообщение об ошибке.

Заключение
Сегодня мы рассмотрели декораторы в Python и несколько примеров их использования. Надеемся, это помогло вам понять базовую концепцию работы декораторов в Python.
