На этом шаге мы рассмотрим решение этой задачи.
К функции был применен декоратор, но вы хотите "отменить" это, чтобы получить доступ к изначальной необернутой функции.
Предполагая, что декоратор был реализован с правильным применением @wraps (см. предыдущий шаг), вы обычно можете получить доступ к оригинальной функции через обращение к атрибуту __wrapped__. Например:
>>> # Декоратор из предыдущего шага >>> @timethis def add(x, y): return x + y >>> >>> orig_add = add.__wrapped__ >>> orig_add(3, 4) 7 >>>
Получение прямого доступа к необернутой функции в обход декоратора может быть полезно для целей отладки, интроспекции и прочих операций с функциями. Однако этот рецепт работает только в том случае, если реализация декоратора правильно копирует метаданные, используя @wraps из модуля functools или напрямую устанавливая атрибут __wrapped__.
Если к функции было применено несколько декораторов, поведение при доступе к __wrapped__ в настоящее время определено следующим образом. Предположим, что у вас есть вот такой код:
>>> from functools import wraps >>> def decorator1(func): @wraps(func) def wrapper(*args, **kwargs): print('Decorator 1') return func(*args, **kwargs) return wrapper >>> def decorator2(func): @wraps(func) def wrapper(*args, **kwargs): print('Decorator 2') return func(*args, **kwargs) return wrapper >>> @decorator1 @decorator2 def add(x, y): return x + y >>> >>> print(add(2, 3)) Decorator 1 Decorator 2 5 >>> print(add.__wrapped__(2, 3)) Decorator 2 5 >>>
И последнее: имейте в виду, что не все декораторы используют @wraps и поэтому могут и не работать так, как описано. В частности, встроенные декораторы @staticmethod и @classmethod создают объекты дескриптора, которые не следуют этим соглашениям (вместо этого они сохраняют изначальную функцию в атрибуте __func__).
На следующем шаге мы рассмотрим определение декоратора, принимающего аргументы.