Декоратор функций в Python — что делает и как создать

D этом уроке вы узнаете о декораторах функций в Python и научитесь создаватьь свои собственные декораторы.

Содержание

Что такое декоратор в Python?

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

Давайте рассмотрим простой пример, чтобы понять концепцию.

Простой пример декоратора Python

Ниже определяется функция net_price:

def net_price(price, tax):
    """ calculate the net price from price and tax
    Arguments:
        price: the selling price
        tax: value added tax or sale tax
    Return
        the net price
    """
    return price *(1 + tax)

Функция net_price вычисляет чистую цену на основе цены продажи и налога. Она возвращает net_price в виде числа. Предположим, что вам нужно отформатировать чистую цену, используя валюту USD. Например, 100 становится 100 долларами. Для этого вы можете использовать декоратор.

По определению декоратор — это функция, которая принимает функцию в качестве аргумента:

def currency(fn):
    pass

И он возвращает еще одну функцию:

def currency(fn):
    def wrapper(*args, **kwargs):
        fn(*args, **kwargs)

    return wrapper

Функция currency возвращает функцию wrapper. Она имеет параметры *args и **kwargs. Эти параметры позволяют вызывать любую функцию fn с любой комбинацией позиционных аргументов и аргументов, состоящих только из ключевых слов.

В этом примере функция wrapper по существу выполняет функцию fn напрямую и не меняет поведение функции fn.

В функции wrapper вы можете вызвать функцию fn, получить ее результат и отформатировать результат как строку валюты:

def currency(fn):
    def wrapper(*args, **kwargs):
        result = fn(*args, **kwargs)
        return f'${result}'
    return wrapper

Функция currency — это декоратор. Она принимает любую функцию, возвращающую число, и форматирует это число как денежную строку.

Чтобы использовать декоратор currency, вам необходимо передать ему функцию net_price, чтобы получить новую функцию и выполнить так, как если бы это была исходная функция. Например:

net_price = currency(net_price)
print(net_price(100, 0.05))

Выход:

$105.0

Определение декоратора Python

Обобщая вышесказанное, декоратор — это:

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

Внутренняя функция является замыканием, поскольку она ссылается на аргумент fn из охватывающей области видимости или функции-декоратора.

Символ @

В предыдущем примере декоратором является currency. И вы можете сделать декоратором функцию net_price, используя следующий синтаксис:

net_price = currency(net_price)

Обычно, если decorate является функцией-декоратором и вы хотите декорировать другую функцию fn, вы можете использовать следующий синтаксис:

fn = decorate(fn)

Чтобы сделать это более удобным, Python предлагает более короткий путь:

@decorate
def fn():
    pass

Например, вместо использования следующего синтаксиса:

net_price = currency(net_price)

… вы можете декорировать функцию net_price с помощью @currency следующим образом:

@currency
def net_price(price, tax):
    """ calculate the net price from price and tax
    Arguments:
        price: the selling price
        tax: value added tax or sale tax
    Return
        the net price
    """
    return price *(1 + tax)

Соединим все это вместе.

def currency(fn):
    def wrapper(*args, **kwargs):
        result = fn(*args, **kwargs)
        return f'${result}'
    return wrapper


@currency
def net_price(price, tax):
    """ calculate the net price from price and tax
    Arguments:
        price: the selling price
        tax: value added tax or sale tax
    Return
        the net price
    """
    return price *(1 + tax)


print(net_price(100, 0.05))

Анализ декорированных функций

Когда вы декорируете функцию:

@decorate
def fn(*args,**kwargs):
    pass

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

fn = decorate(fn)

Функция декорирования возвращает новую функцию, которая является функцией-оберткой.

Если вы используете встроенную функцию справки для отображения документации новой функции, вы не увидите документацию исходной функции. Например:

help(net_price)

Выход:

wrapper(*args, **kwargs)

None

Кроме того, если вы проверите имя новой функции, Python вернет имя внутренней функции, возвращенной декоратором:

print(net_price.__name__)

Выход:

wrapper

Поэтому, когда вы декорируете функцию, вы теряете исходную сигнатуру функции и документацию.

Чтобы исправить это, вы можете использовать функцию wraps из стандартного модуля functools. По сути, функция wraps также является декоратором.

Ниже показано, как использовать декоратор wraps:

from functools import wraps


def currency(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
        result = fn(*args, **kwargs)
        return f'${result}'
    return wrapper


@currency
def net_price(price, tax):
    """ calculate the net price from price and tax
    Arguments:
        price: the selling price
        tax: value added tax or sale tax
    Return
        the net price
    """
    return price *(1 + tax)


help(net_price)
print(net_price.__name__)

Выход:

net_price(price, tax)
    calculate the net price from price and tax
    Arguments:
        price: the selling price
        tax: value added tax or sale tax
    Return
        the net price

net_price
Похожие посты
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *