wraps
копирует ряд атрибутов из другой функции в эту функцию - по умолчанию __module__
, __name__
, __qualname__
, __annotations__
и __doc__
.
Наиболее очевидно полезный для копирования это __doc__
. Рассмотрим этот более простой пример: 1
class Base:
def spam(self, breakfast):
"""spam(self, breakfast) -> breakfast with added spam
<29 lines of detailed information here>
"""
class Child:
@functools.wraps(Base.spam)
def spam(self, breakfast):
newbreakfast = breakfast.copy()
newbreakfast.meats['spam'] + 30
return newbreakfast
Теперь, если кто-то захочет использовать help(mychild.spam)
, он получит 29 строк полезной информации. (Или, если они автоматически заполняют mychild.spam
в PyCharm, всплывет оверлей с документацией и т. Д.) Все без того, чтобы мне пришлось вручную копировать и вставлять его. И, что еще лучше, если Base
получен из какой-то среды, которую я не писал, и мой пользователь обновит версию 1.2.3 до 1.2.4 этой платформы, и появится лучшая строка документации, они увидят эту лучшую строку документации.
В наиболее распространенном случае Child
будет подклассом Base
, а spam
будет переопределением. 2 Но это на самом деле не требуется - wraps
не делает не волнует, используете ли вы подтип с помощью наследования, или типизацию утили, просто реализуя неявный протокол; это одинаково полезно для обоих случаев. Поскольку Child
предназначен для реализации протокола spam
из Base
, имеет смысл для Child.spam
иметь ту же строку документации (и, возможно, другие атрибуты метаданных).
Другие атрибуты, вероятно, не так полезны, как строки документов. Например, если вы используете аннотации типов, их полезность в чтении кода, вероятно, по меньшей мере столь же высока, как и возможность запускать Mypy для статической проверки типов, поэтому простое их динамическое копирование из другого метода часто не все это полезно. И __module__
и __qualname__
в основном используются для размышлений / осмотров, и в этом случае, скорее всего, будут вводить в заблуждение, чем полезны (хотя вы, вероятно, могли бы придумать пример структуры, где вы бы хотели, чтобы люди читали код в Base
вместо кода в Child
, что не так для очевидного примера по умолчанию). Но, если они не являются активно вредными, стоимость читабельности использования @functools.wraps(Base.spam, assigned=('__doc__',))
вместо просто значений по умолчанию может не стоить этого.
1. Если вы используете Python 2, измените эти классы для наследования от object
; в противном случае это будут классы старого стиля, которые просто усложняют ситуацию. В Python 3 классы старого стиля отсутствуют, поэтому эта проблема даже не возникает.
2. Или, может быть, «виртуальный подкласс» ABC, объявленный с помощью вызова register
или через ловушку подкласса.