Большим удобством при работе с Python, особенно в интерактивной оболочке, является его мощная способность к самоанализу. Интроспекция - это способность объекта узнавать о своих собственных атрибутах во время выполнения. Например, функция знает свое собственное имя и документацию:
>>> print # <built-in function print> >>> print.__name__ # 'print' >>> help(print) # Help on built-in function print in module builtins: # print(...) # print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False) # Prints the values to a stream, or to sys.stdout by default. # Optional keyword arguments: # file: a file-like object (stream); defaults to the current sys.stdout. # sep: string inserted between values, default a space. # end: string appended after the last value, default a newline. # flush: whether to forcibly flush the stream.
Интроспекция работает и для функций, которые вы сами определяете. Для примера будем использовать файл decorator.py
из материала "Возврат значений из декорируемой функции":
from decorator import talk @talk def say(name): """Функция печатает приветствие name: строка с именем для приветствия """ return f'Привет {name}.' >>> say # <function talk.<locals>.wrapper at 0x7fd696ce18c8> >>> say.__name__ # 'wrapper' >>> help(say) # Help on function wrapper in module decorator: # wrapper(*args, **kwargs) # (END)
После того, как к функции say()
применен декоратор, её атрибут __name__
изменился на имя внутренней функции wrapper()
декоратора @talk
. Хотя технически это верно, но это не очень полезная информация для IDE и процесса отладки программы .
Чтобы предотвратить смену атрибута __name__
и документации декорируемой функции, декораторы должны использовать декоратор @functools.wraps
, который сохранит информацию о первоначальной функции. Изменим декоратор, сохраненный в файле decorator.py
следующим образом:
import functools def talk(func): @functools.wraps(func) def wrapper(*args, **kwargs): dash = '-'*15 string = func(*args, **kwargs) return dash + '\n' + string + '\n' + dash return wrapper
Декорируемая функция say()
остается без изменений. Мы знаем, что операторы и функций определенные в модуле инициализируются в момент импорта модуля и выполняются только один раз, следовательно, что бы изменения, сделанные в файле модуля декоратора decorator.py
вступили в силу, нужно выйти и снова зайти в интерактивный режим и произвести все операции заново или перезагрузить модуль декоратора не выходя из интерактивного режима и переопределить функцию say()
с перезагруженным декоратором @talk
.
# перезагрузка декоратора import importlib, sys talk = importlib.reload(sys.modules['decorator']).talk # переопределение функции @talk def say(name): """Функция печатает приветствие name: строка с именем для приветствия """ return f'Привет {name}.' # пробуем >>> say # <function say at 0x7fbc486b8158> >>> say.__name__ # 'say' >>> help(say) # Help on function say in module __main__: # say(name) # Функция печатает приветствие # name: строка с именем для приветствия # (END)
Теперь у функции say()
атрибут __name__
и документация не теряются после применения декоратора.
Декоратор @functools.wraps
использует функцию functools.update_wrapper()
для обновления специальных атрибутов __name__
и __doc__
.