Автор оригинала: Moyosore Sosan.
В предыдущем посте мы говорили о локальных функциях , закрытиях и быстро изучили базовый декоратор. В этом посте мы расскажем о декораторах.
Декораторы
Как упоминалось ранее, декоратор сам по себе является вызываемым объектом, который принимает другой вызываемый объект и возвращает другой вызываемый объект, т. Е. функцию, которая принимает другую функцию в качестве аргумента и возвращает функцию. Пример:
def capitalize(func): def uppercase(): result = func() return result.upper() return uppercase @capitalize def say_hello(): return "hello" print(say_hello()) # 'HELLO'
@capitalize
– это наш декоратор, который принимает say_hello()
и возвращает верхний регистр
. Давайте рассмотрим другой пример
def square(func): def multiply(*args): f = func(*args) return f * f return multiply @square def addition(x,y): return x + y print(addition(5,7)) # 144 print(addition(15,10)) # 625
Декоратор square применяется к функции addition , это означает, что результат вызова нашей функции addition будет передан в square в качестве аргумента. Результирующей функцией от этого является функция multiply , следовательно, результат сложения умножается сам на себя.
Декораторы с аргументами
Помните, что декоратор-это вызываемая функция, это позволяет нам передавать аргументы нашим декораторам. Аргументы передаются в декораторы так же, как они могут быть переданы в обычные функции.
# Parsing arguments to a normal fuction normal_function(arg1, arg2) normal_function(arg1='Hello', arg2='Dear') # Passing arguments into a decorator @my_decorator(arg1, arg2) def my_function(): pass @my_decorator(arg1='Hello', arg2='World') def my_function(): pass
Декораторы могут быть написаны так, чтобы принимать как позиционные, так и ключевые аргументы, как и любая другая функция.
Чтобы декоратор принимал аргументы, он включается в реализацию для такого декоратора.
def decorator(arg1, arg2): def main_decorator(func): def func_wrapper(*args, **kwargs): f = func(*args, **kwargs) return 'Hi {0}, {1} {2}'.format(f, arg1, arg2) return func_wrapper return main_decorator @decorator('hello', 'dear') def my_function(name): return name print(my_function('Emma')) # 'Hi Emma, hello dear'
Функция decorator создается с аргументами и оборачивается вокруг фактического декоратора main_decorator , который выполняет основную функцию, а затем передает результат в качестве возвращаемого значения decorator
Давайте рассмотрим другой пример, на этот раз с аргументом ключевого слова и выводом, основанным на переданном аргументе.
def greet(time='day'): print(time.lower()) def greet_decorator(func): def greet_person(*args, **kwargs): person = func(*args, **kwargs) return "Good {0} {1}".format(time, person) return greet_person return greet_decorator @greet() def get_name(name): return name @greet('Morning') def greet_name(name): return name @greet(time='Evening') def name(name): return name print(get_name('Josh')) # Good day Josh print(greet_name('Aduke')) # Good Morning Aduke print(name('Chima')) # Good Evening Chima
Как объяснялось ранее, great декораторы принимают аргумент и возвращают результат, основанный на этом.
Несколько декораторов
Функция может быть украшена несколькими декораторами. Для этого мы складываем декораторы на функции. Эти декораторы по очереди применяются к функции, причем сначала применяется тот, который находится непосредственно поверх функции
@decorator1 @decorator2 @decorator3 def my_function(): pass
Из приведенного выше примера. декоратор 3 сначала применяется к my_function , затем декоратор 2 и, наконец, декоратор 1. Пример:
def uppercase(func): def wrap(): f = func() return f.upper() return wrap def split_string(func): def wrap(): f = func() return f.split() return wrap @split_string @uppercase def hello(): return 'hello world' print(hello()) # ['HELLO', 'WORLD']
Функция hello() украшена двумя функциями: верхний регистр и split_string . Эти два декоратора применяются к функции hello один за другим. @верхний регистр применяется первым, затем результат передается в @split_string Функция hello возвращает строку hello word
, когда применяется @верхний регистр , в качестве вывода у нас есть HELLO WORLD
. Этот вывод затем передается в @split_string , что приводит к ['HELLO', 'WORLD']
. Если в функции hello было больше декораторов, цепочка продолжается до последнего.
Функция || hello() || украшена двумя функциями: || верхний регистр || и || split_string || . Эти два декоратора применяются к функции || hello || один за другим. || @верхний регистр || применяется первым, затем результат передается в || @split_string || Функция || hello || возвращает строку || hello word || , когда применяется || @верхний регистр||, в качестве вывода у нас есть || HELLO WORLD||. Этот вывод затем передается в || @split_string||, что приводит к || [‘HELLO’, ‘WORLD’] || . || Если в функции hello было больше декораторов, цепочка продолжается до последнего.
Строки документов Python обеспечивают удобный способ документирования модулей, функций, классов и методов Python. В python функция help
выводит связанную строку документа для функции. Tpying help(range)
в python repl предоставляет соответствующую строку документа для встроенной функции python range , как показано на рисунке:
Функции имеют такие атрибуты, как:
- __name__: имя функции, как определено
- __doc__: Строка документа, связанная с функцией Мы можем определить строки документов для наших функций
def doc_string(): """ Docstrings help document python functions This is the docstring associated with this function """ return "I have a docstring" print (doc_string()) # 'I have a docstring' print (doc_string.__name__) # doc_string print (doc_string.__doc__) # Docstrings help document python functions # This is the docstring associated with this function print (help(doc_string)) # Help on function doc_string: # doc_string() # Docstrings help document python functions # This is the docstring associated with this function
Теперь давайте применим ранее определенный @верхний регистр к нашей doc_string функции и выведем __name__ и __doc__ атрибут для doc_string
@uppercase def doc_string(): """ Docstrings help document python functions This is the docstring associated with this function """ return "I have a docstring" print (doc_string()) # 'I HAVE A DOCSTRING' print (doc_string.__name__) # wrap print (doc_string.__doc__) # wrap() print (help(doc_string)) # Help on function wrap: # wrap()
Мы видим, что у нас есть другой атрибут __name__ и __doc__ для функции doc_string . Обратите внимание, что он принимает атрибуты из функции декоратора, это потому, что мы заменили функцию doc_string функцией декоратора.
Чтобы функция doc_string сохранила свои исходные атрибуты, мы используем метод wraps
в пакете functools
python. функции.wraps
помогает нам исправить метаданные оформленной функции.
Теперь давайте рефакторингуем наш верхний регистр декоратор для использования functools.обертывает
и применяет его к doc_string()
import functools def uppercase(func): @functools.wraps(func) def wrap(): f = func() return f.upper() return wrap
С помощью @functools.при применении наша функция doc_string может сохранить свой атрибут
@uppercase def doc_string(): """ Docstrings help document python functions This is the docstring associated with this function """ return "I have a docstring" print (doc_string()) # 'I have a docstring' print (doc_string.__name__) # doc_string print (doc_string.__doc__) # Docstrings help document python functions # This is the docstring associated with this function print (help(doc_string)) # Help on function doc_string: # doc_string() # Docstrings help document python functions # This is the docstring associated with this function
Зачем использовать декораторы
- Они мощные и широко используются в python
- Они улучшают четкость кода
- Они снижают сложность кода