Декоратор функций в Python — что делает и как создать
D этом уроке вы узнаете о декораторах функций в Python и научитесь создаватьь свои собственные декораторы.
- Что такое декоратор в Python?
- Простой пример декоратора Python
- Определение декоратора 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