Шаг 158.
Python: сборник рецептов.
Метапрограммирование. Снятие ("разворачивание") декоратора

    На этом шаге мы рассмотрим решение этой задачи.

Задача

    К функции был применен декоратор, но вы хотите "отменить" это, чтобы получить доступ к изначальной необернутой функции.

Решение

    Предполагая, что декоратор был реализован с правильным применением @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__).

    На следующем шаге мы рассмотрим определение декоратора, принимающего аргументы.




Предыдущий шаг Содержание Следующий шаг