Декораторы в Python
С помощью декоратора можно дать функции дополнительное поведение, не трогая её код.
Декоратор сам является функцией. Он принимает функцию на вход и возвращает новую.
Простой декоратор
Сначала определите декоратор, а затем напишите @decorator_name в строке над функцией, к которой хотите его применить.
В этом примере строка @changecase прямо над myfunction «декорирует» её с помощью changecase.
Значит, changecase — это декоратор.
А myfunction — это функция, которую декорируют.
Пример
Этот декоратор переводит в верхний регистр всё, что возвращает декорированная функция.
changecase, который переводит возвращаемое значение функции в верхний регистр.
Примените его к функции, которая возвращает "Hello Sally", и выведите результат.
Вывод: полная программа на Python
def changecase(func):
def myinner():
return func().upper()
return myinner
@changecase
def myfunction():
return "Hello Sally"
print(myfunction())
Многократное использование декоратора
Один декоратор можно использовать сколько угодно раз. Просто поместите его над каждой функцией, которой он нужен.
Пример
Один и тот же декоратор @changecase, применённый к двум функциям:
changecase, который переводит возвращаемые значения в верхний регистр.
Примените его к двум разным функциям и выведите оба результата.
Вывод: полная программа на Python
def changecase(func):
def myinner():
return func().upper()
return myinner
@changecase
def myfunction():
return "Hello Sally"
@changecase
def otherfunction():
return "I am speed!"
print(myfunction())
print(otherfunction())
Аргументы декорированной функции
Функцию, которой нужны аргументы, тоже можно декорировать, если функция-обёртка передаёт эти аргументы дальше:
Пример
Декорирование функции, которая принимает аргумент:
changecase, который работает с функцией, принимающей один аргумент.
Декорированная функция должна возвращать приветствие в верхнем регистре.
Вывод: полная программа на Python
def changecase(func):
def myinner(x):
return func(x).upper()
return myinner
@changecase
def myfunction(nam):
return "Hello " + nam
print(myfunction("John"))
*args и **kwargs
Декоратор не всегда может знать, какие аргументы получит декорированная функция. Решение — дать функции-обёртке параметры (*args, **kwargs). Тогда она сможет принимать любое количество аргументов любого типа и передавать их декорированной функции.
Пример
Делаем обёртку безопасной с помощью *args и **kwargs:
changecase, использующий *args и **kwargs для гибкости.
Примените его к функции, которая принимает параметр с именем.
Вывод: полная программа на Python
def changecase(func):
def myinner(*args, **kwargs):
return func(*args, **kwargs).upper()
return myinner
@changecase
def myfunction(nam):
return "Hello " + nam
print(myfunction("John"))
Декоратор с аргументами
Чтобы дать декоратору собственные аргументы, оберните его ещё в один уровень функции.
Пример
Фабрика декораторов: полученный аргумент определяет, в каком регистре будет результат.
changecase, которая принимает аргумент n.
Если n==1, переведите результат в нижний регистр; иначе — в верхний.
Вывод: полная программа на Python
def changecase(n):
def changecase(func):
def myinner():
if n == 1:
a = func().lower()
else:
a = func().upper()
return a
return myinner
return changecase
@changecase(1)
def myfunction():
return "Hello Linus"
print(myfunction())
Несколько декораторов
У одной функции может быть несколько декораторов.
Просто разместите вызовы декораторов один над другим.
Они применяются в обратном порядке: первым идёт декоратор, ближайший к функции.
Пример
Один декоратор добавляет приветствие, а другой переводит текст в верхний регистр:
changecase (верхний регистр) и addgreeting (добавляет текст приветствия).
Примените оба к одной функции и выведите результат.
Вывод: полная программа на Python
def changecase(func):
def myinner():
return func().upper()
return myinner
def addgreeting(func):
def myinner():
return "Hello " + func() + " Have a good day!"
return myinner
@changecase
@addgreeting
def myfunction():
return "Tobias"
print(myfunction())
Сохранение метаданных функции
У каждой функции в Python есть метаданные, которые можно прочитать через её атрибуты __name__ и __doc__.
Но при декорировании функции эти метаданные теряются, и исходные значения становятся недоступны.
В стандартной библиотеке есть решение: функция functools.wraps сохраняет имя и строку документации (docstring) исходной функции.
Пример
Функция без декоратора сообщает своё имя через __name__:
__name__.
Вывод: полная программа на Python
def myfunction():
return "Have a great day!"
print(myfunction.__name__)
Пример
Спросите у декорированной функции её имя, и ответ будет другим:
__name__.
Выведите __name__ декорированной функции.
Вывод: полная программа на Python
def changecase(func):
def myinner():
return func().upper()
return myinner
@changecase
def myfunction():
return "Have a great day!"
print(myfunction.__name__)
Пример
Если импортировать и применить functools.wraps, исходные имя и строка документации сохраняются.
functools.wraps, чтобы сохранить исходное имя функции при декорировании.
Выведите __name__ декорированной функции.
Вывод: полная программа на Python
import functools
def changecase(func):
@functools.wraps(func)
def myinner():
return func().upper()
return myinner
@changecase
def myfunction():
return "Have a great day!"
print(myfunction.__name__)