LINUX.ORG.RU

Python, идентификация декорированной функции.

 


0

1

Есть некий набор декораторов(выполняющих схожие, но немного разные проверки), которые по своей логике работы могут применяться к одной функции несколько раз.

И вот, чтобы не накручивались тысячи обёрток на одну функцию(а там внутри функция может вызываться несколько раз) сразу приходит в голову идея: Добавляем к функции-обёртке некий атрибут, по которому в следующих декораторах можно будет определить, что функция уже декорирована и нужно просто дополнить/изменить параметры этой обёртки.

например:

def decorator(f):
    if hasattr(f, 'iam_decorated'):
        f.__params.add_new_params(something_other)
        return f
    else:
        @wraps(f)
        def newf(*args, **kwargs):
            work_with_params( newf.__params )
            return f(*args, **kwargs)
        newf.iam_decorated = True
        newf.__params = new_params(something)
        return newf

Но, в таком случае может возникнуть ситуация, когда функцию продекорировали каким-то сторонним декоратором, который не в курсе про наши проблемы и потому функция, декорированная этим декоратором, при применении к ней нашего декоратора будет воспринята как новая функция, что не верно.

Тогда приходит в голову следующая идея: содержать хэш, ключом в котором будет какая-то информация, идентифицирующая функцию, а значением — нужные параметры.

Вопрос в том, есть ли такая информация, которая сохраняется даже после применения к функции какого-либо декоратора(т.е. переходит от оригинальной функции к функции-обёртке)?

Например, вроде как, принятой хорошей практикой является использование @functools.wraps. Т.е. копируется __name__ и __doc__. Вот казалось бы мне этого было бы достаточно(а на плохие декораторы, которые не используют wraps, положим, покачто). Но __doc__ установлен далеко не в каждой функции. А __name__ далеко не всегда уникально.

Есть что-нибудь?

★★★★★

Последнее исправление: Bad_ptr (всего исправлений: 2)

можно переопределить @functools.wraps на свою реализацию и делать там что хочешь.

mashina ★★★★★
()

но вообще лучше передизайнить api так, чтобы исходной проблемы с каскадированием декораторов не было. Много декораторов это костыли и зло.

mashina ★★★★★
()

Думаю что нет т.к. декораторы это просто синтаксический сахар. Но я щас покопаюсь в атрибутах, может что найдётся....

true_admin ★★★★★
()
Ответ на: комментарий от mashina

Поддерживаю, возможно, задачу можно решить по-другому. Может вместо декораторов регистрировать свои обработчики? Типа плагинов там, итп.

true_admin ★★★★★
()

Связанные декораторы это очень плохо. Нога уже на мушке.

baverman ★★★
()
Ответ на: комментарий от true_admin

ладно, ок, спасибо, за предложенные варианты.
Ну в принципе ещё попробую inspect'ом потыкать, но похоже что да, так не получится.

Bad_ptr ★★★★★
() автор топика
Ответ на: комментарий от true_admin

ЕМНИП исходная ф-я висит на замыкании. Дорыться то до нее не проблема, вычленить в принципе тоже, есть строка байт-кода и всякое такое - хэшируй ... другой вопрос, что сама задача странная.

Лучше ИМНО написать некий супер-декоратор, коллекционирующий проверки или что там есть. При вызове любого декоратора из семейства, либо накручивается супер-декоратор (если его не было), либо к уже существующему добавляются доп. проверки.

И я бы не стал выпендриваться, а для супер-декоратора сделал бы класс с перегруженным __call__.

AIv ★★★★★
()

Вот, кстати, что ещё обнаружил — functools.wraps добавляет к функции-обёртке атрибут __wrapped__ указывающий на оригинальную функцию. Так что если принять, что все декораторы используют functools.wraps, то можно добраться до нужного «слоя обёрток» по цепочке, через __wrapped__. :)

Bad_ptr ★★★★★
() автор топика
Ответ на: комментарий от Bad_ptr

Так что если принять, что все декораторы используют functools.wraps

В реальной жизни это не всегда так.

baverman ★★★
()

Если ты делаешь для себя, это еще имеет какой-то смысл. Если нет то всем пофиг, поэтому лучше придерживаться правила «декоратор самодостаточная единица».

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.