Рубрики
Без рубрики

Погружение В Закрытие и декораторы Python – Часть 2

В предыдущем посте (https://www.codementor.io/moyosore/a-dive-into-python-closures-and-decorators-part-1-9mpr98pgr), мы поговорили о локальных функциях, закрытиях и быстро изучили основные…

Автор оригинала: 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 , как показано на рисунке:

Снимок экрана 2017-08-01 в 12.21.33 вечера.png
Снимок экрана 2017-08-01 в 12.16.30 вечера.png

Функции имеют такие атрибуты, как:

  • __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
  • Они улучшают четкость кода
  • Они снижают сложность кода