Декораторы


Декораторы позволяют вносить простые изменения в вызываемые объекты, такие как функции , методы или классы . В этом уроке мы разберемся с функциями. Синтаксис

@decorator def functions(arg): return "value"
1
2
3
@ декоратор
функции def ( arg ) :
вернуть "значение"
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
В 1]:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1/0 0/0

Эквивалентно:

def function(arg): return "value" function = decorator(function) # this passes the function to the decorator, and reassigns it to the functions
1
2
3
Функция def ( аргумент ) :
вернуть "значение"
function = decorator ( function ) # это передает
функции декоратора и переназначает ее на
функции
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
В 1]:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1/0 0/0

Как вы могли заметить, декоратор - это просто еще одна функция, которая принимает функцию и возвращает ее. Например, вы могли бы сделать это:

def repeater(old_function): def new_function(*args, **kwds): # See learnpython.org/en/Multiple%20Function%20Arguments for how *args and **kwds works old_function(*args, **kwds) # we run the old function old_function(*args, **kwds) # we do it twice return new_function # we have to return the new_function, or it wouldn't reassign it to the value
1
2
3
4
5
Защита повторитель ( old_function ) :
def new_function ( * args , ** kwds ) : # См. learnpython
.org / en / Несколько% 20Function% 20Аргументы для того, как * args
и ** kwds работает
old_function ( * args , ** kwds ) # запускаем старую
функция
old_function ( * args , ** kwds ) # делаем это дважды
return new_function # мы должны вернуть
new_function, иначе он не переназначил бы ее значению
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
В 1]:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1/0 0/0

Это заставит функцию повторяться дважды.

>>> @repeater def multiply(num1, num2): print(num1 * num2) >>> multiply(2, 3) 6 6
1
2
3
4
5
6
7
>>> @ повторитель
Защита умножения ( num1 , num2 ) :
печать ( num1 * NUM2 )
>>> умножить ( 2 , 3 )
6
6
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
В 1]:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1/0 0/0

Вы также можете изменить вывод

def double_out(old_function): def new_function(*args, **kwds): return 2 * old_function(*args, **kwds) # modify the return value return new_function
1
2
3
4
def double_out ( старая_функция ) :
def new_function ( * args , ** kwds ) :
return 2 * old_function ( * args , ** kwds ) #
изменить возвращаемое значение
вернуть новую_функцию
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
В 1]:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1/0 0/0

изменить ввод

def double_Ii(old_function): def new_function(arg): # only works if the old function has one argument return old_function(arg * 2) # modify the argument passed return new_function
1
2
3
4
def double_Ii ( старая_функция ) :
def new_function ( arg ) : # работает, только если старый
функция имеет один аргумент
return old_function ( arg * 2 ) # изменить
аргумент передан
вернуть новую_функцию
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
В 1]:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1/0 0/0

и делаем проверку.

def check(old_function): def new_function(arg): if arg < 0: raise (ValueError, "Negative Argument") # This causes an error, which is better than it doing the wrong thing old_function(arg) return new_function
1
2
3
4
5
def check ( old_function ) :
def новая_функция ( аргумент ) :
если arg < 0 : raise ( ValueError , "Отрицательное
Аргумент " ) # Это вызывает ошибку, что лучше
чем он делает неправильные вещи
старая_функция ( аргумент )
вернуть новую_функцию
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
В 1]:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1/0 0/0

Допустим, вы хотите умножить результат на переменную величину. Вы можете определить декоратор и использовать его следующим образом:

def multiply(multiplier): def multiply_generator(old_function): def new_function(*args, **kwds): return multiplier * old_function(*args, **kwds) return new_function return multiply_generator # it returns the new generator # Usage @multiply(3) # multiply is not a generator, but multiply(3) is def return_num(num): return num # Now return_num is decorated and reassigned into itself return_num(5) # should return 15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def multiply ( множитель ) :
def multiply_generator ( старая_функция ) :
def new_function ( * args , ** kwds ) :
вернуть множитель * old_function ( * args ,
** кВт )
вернуть новую_функцию
return multiply_generator # возвращает новый
генератор
# Использование
@ multiply ( 3 ) # multiply - это не генератор, а
умножить (3)
def return_num ( число ) :
вернуть номер
# Теперь return_num оформлен и переназначен в
сам
return_num ( 5 ) # должен вернуть 15
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
В 1]:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
1/0 0/0

Вы можете делать все, что захотите, со старой функцией, даже полностью игнорируя ее! Продвинутые декораторы также могут управлять строкой документа и номером аргумента. Для некоторых шикарных декораторов перейдите на http://wiki.python.org/moin/PythonDecoratorLibrary .

Упражнение

Создайте фабрику декораторов, которая возвращает декоратор, украшающий функции одним аргументом. Фабрика должна принимать один аргумент, тип, а затем возвращать декоратор, который заставляет функцию проверять, является ли ввод правильным типом. Если это неверно, он должен напечатать («Плохой тип») (на самом деле это должно вызвать ошибку, но в этом руководстве нет сообщения об ошибке). Посмотрите на учебный код и ожидаемый результат, чтобы увидеть, что это такое, если вы запутались (я знаю, что буду). Использование isinstance (object, type_of_object) или type (object) может помочь.