Содержание
1. Что такое функции?
2. Что такое замыкание?
3. Декораторы
4. Реальный пример
5. Декораторы, принимающие аргументы

Прежде чем иметь дело с декораторами, важны две другие концепции Python: функции и замыкание.

1. Что такое функции?

Функции такие же, как и любой другой тип данных, в том смысле, например, что вы можете присвоить его переменной, а затем вызвать эту переменную (вызов функции). Вы также можете передать одну функцию другой или даже вернуть функцию из функции.

def funct():
  print('Hi!')

x = funct
x()  #Prints 'Hi!'

2. Что такое закрытие?

Закрытие — это способ убедиться, что функция имеет параметры, необходимые для запуска. Это кортеж переменных, которые больше не доступны в области видимости.

def funct(x):
  a = 5

  def inner_funct(x):
    print(a)
  
  return inner_funct

call_func = funct() # Assigns to inner_funct
call_func()  # Prints 5

Как объяснить, что inner_funct (call_func) запоминает значение a, если оно было определено во внешней функции? Когда func возвращает функцию inner_funct, она прикрепляет все, что функция использует, к объекту функции. Вы можете получить доступ к этим значениям следующим образом:

type(call_func.__closure__)   # A tuple
print(call_func.__closure__[0].cell_contents) # Access the actual value

3. Декораторы

Декораторы — это просто функции, которые принимают одну функцию в качестве аргумента и возвращают модифицированную версию этой функции. Для начала давайте разберемся с функцией, которая получает в качестве аргумента другую и возвращает функцию.

def mult(a, b):
  prints(a * b)

def dec_funct(func):
  def wrapper(a, b):
    return mult(a * 2, b * 2)
  return wrapper

new_mult = dec_funct(mult)
new_mult(1, 5)  # Prints 20

Анализируя код выше, мы имеем следующее: во-первых, у нас есть функция mult, которая будет оформлена. Эта функция просто умножает два числа, переданные в качестве аргументов.

Позже у нас есть dec_funct, который работает как наш декоратор. Он берет функцию, модифицирует ее и возвращает другую. Чтобы иметь возможность возвращать функцию, внутри создается новая функция: функция-оболочка. Функция-оболочка возвращает функцию, оформленную (mult) с небольшим изменением (удвоением аргументов).

Когда new_mult назначается dec_funt(mult), объект теперь является функцией, которая умножает параметры на два, а затем умножает их. Вызов new_mult(1, 5) вызывает измененную версию функции mult. Вместо new_mult мы можем просто переопределить mult без каких-либо проблем, потому что определение функции хранится в замыкании новой функции.

mult = dec_funct(mult)
mult(1, 5)  # Prints 20

Код, построенный на данный момент, эквивалентен приведенному ниже коду с использованием соответствующего синтаксиса для декораторов:

def dec_funct(func):
  def wrapper(a, b):
    return mult(a * 2, b * 2)
  return wrapper

@dec_funct
def mult(a, b):
  prints(a * b)

mult(1, 5)  # Prints 20

Декорирование функции аналогично вызову функции-декоратора с декорированной функцией в качестве аргумента и присвоению ее самой себе.

4. Реальный пример

Декоратор ниже умножает, сколько нужно, чтобы суммировать от 1 до 100 000.

import time
def timer(func):
  def wrapper(*args, **kwargs):   # Arguments to work with any function
    t_start = time.time()
    result = func(*args, **kwargs)
    t_total = time.time() - t_start
    print(f'The total time for running was {t_total:.2}.')
    return result
  return wrapper

@timer
def count():
  for i in range(1, 100000):
    i += 1

count() 

5. Декораторы, принимающие аргументы

Декоратор, принимающий аргументы, — это функция, возвращающая декоратор, а не функция, которая является единицей.

def run_n_times(n):        # Returns a decorator 
  def decorator(func):     # Decorator function
    def wrapper(*args, **kwargs):  
      for i in range(n):
        func(*args, **kwargs)
      return wrapper
  return decorator

@run_n_times(5)    # Runs the function 5 times
def prints():
  print('Printing')

Он устроен таким образом, что функция оболочки имеет доступ к переменной, переданной в run_n_times(n). При вызове @run_n_times(5) мы украшаем функцию prints результатом этого вызова (декоратором функции).

______________
Это мои учебные записи. Я надеюсь, что это может быть полезно для вас, но остерегайтесь любых ошибок, которые вы можете совершить.
Давайте подключимся к LinkedIn!