90 Глава 3 •
Эффективные функции
Например, оригинальное имя функции, ее строка документации docstring
и список параметров скрыты замыканием-оберткой:
def greet():
"""Вернуть дружеское приветствие."""
return 'Привет!'
decorated_greet = uppercase(greet)
При попытке получить доступ к каким-либо из этих метаданных функции
вместо них вы увидите метаданные замыкания-обертки:
>>> greet.__name__
'greet'
>>> greet.__doc__
'Вернуть дружеское приветствие.'
>>> decorated_greet.__name__
'wrapper'
>>> decorated_greet.__doc__
None
Это делает отладку и работу с интерпретатором Python неуклюжей и тру-
доемкой. К счастью, существует быстрое решение этой проблемы: деко-
ратор
functools.wraps
, включенный в стандартную библиотеку Python
1
.
Декоратор
functools.wraps
можно использовать в своих собственных
декораторах для того, чтобы копировать потерянные метаданные из не-
декорированной функции в замыкание декоратора. Вот пример:
import functools
def uppercase(func):
@functools.wraps(func)
def wrapper():
return func().upper()
return wrapper
Применение декоратора
functools.wraps
к замыканию-обертке, возвра-
щаемому декоратором, переносит в него строку документации и другие
метаданные входной функции:
1
См. документацию Python «functools»:
https://docs .python .org/3/library/functools .html
3 .3 . Сила декораторов
91
@uppercase def greet():
"""Вернуть дружеское приветствие."""
return 'Привет!'
>>> greet.__name__
'greet'
>>> greet.__doc__
'Вернуть дружеское приветствие.'
В качестве оптимального практического приема я порекомендовал бы ис-
пользовать декоратор
functools.wraps
во всех декораторах, которые вы
пишете сами. Это не займет много времени и уменьшит головную боль
вам (и другим) в будущем при отладке.
Да, и поздравляю — вы успешно добрались до самого конца этого слож-
ного раздела и многое узнали о декораторах в Python. Отличная работа!
Ключевые
выводы
Декораторы определяют структурные блоки многократного исполь-
зования, которые можно применять к вызываемому объекту с целью
модификации его поведения без необратимого изменения самого вы-
зываемого объекта.
Синтаксис
@
является всего-навсего сокращенной записью для вызова
декоратора с входной функцией. Многочисленные декораторы, разме-
щенные над одной-единственной функцией, применяются снизу-вверх
(
стековая укладка декораторов).
В качестве оптимального практического приема отладки исполь-
зуйте в своих собственных декораторах вспомогательный декоратор
functools.wraps
, чтобы переносить метаданные из недекорированного
вызываемого объекта в декорированный.
Точно так же, как и любой другой инструмент в комплекте инструмен-
тов разработки программного обеспечения, декораторы не являются
панацеей, и ими не стоит злоупотреблять. Важно уравновесить необ-
ходимость «довести дело до конца» с целевой установкой «не увязнуть
в ужасной и неудобной в обслуживании мешанине кодовой базы».
92 Глава 3 • Эффективные функции
3 .4 . Веселье с *args и **kwargs
Однажды я программировал в паре с суровым питонистом, который то
и дело восклицал «арррг!» и «кварг!» всякий раз, когда набирал опре-
деление функции с необязательными или именованными параметрами.
Во всем остальном мы отлично поладили. М-да, вот что в итоге делает
с людьми программирование в академии...
Нужно сказать, что сколько бы ни потешались над параметрами
*args
и
**kwargs
, тем не менее они являются очень полезным функциональным
средством языка Python. И понимание их потенциала сделает из вас более
эффективного разработчика.
Итак, для чего же используются параметры
*args
и
**kwargs
? Они по-
зволяют функции принимать
необязательные аргументы, благодаря чему
вы можете создавать гибкие API в модулях и классах:
def foo(required, *args, **kwargs):
print(required)
if args:
print(args)
if kwargs:
print(kwargs)
Приведенная выше функция требует по крайней мере одного аргумента
под названием «
required
», то есть обязательный, но она также может при-
нимать дополнительные позиционные и именованные аргументы.
Если мы вызовем функцию с дополнительными аргументами, то
args
соберет дополнительные позиционные аргументы в кортеж, потому что
имя
параметра имеет префикс
*
.
Аналогичным образом,
kwargs
соберет дополнительные именованные
аргументы в словарь, потому что
имя параметра имеет префикс
**
.
Как
args
, так и
kwargs
могут быть пустыми, если никакие дополнительные
аргументы в функцию не переданы.
Когда мы вызываем функцию с различными комбинациями аргументов,
вы видите, как Python собирает их в параметрах
args
и
kwargs
в соответ-
3 .4 . Веселье с *args и **kwargs
93
ствии с тем, являются они позиционными или именованными аргумен-
тами:
>>> foo()
Do'stlaringiz bilan baham: