Как стать автором
Обновить

Добавляем чуть больше рефлексии: декораторы

Время на прочтение3 мин
Количество просмотров9.3K
Последнее время приходится довольно много работать с Python. Решая одну из текущих задач, возникла необходимость внутри функции-декоратора проверить задекорирован ли декорируемый метод другим декоратором. К сожалению, стандартные средства рефлексии языка не позволяют это сделать. Точнее, используя, например, модуль inspect из стандартной библиотеки это сделать можно, но уж больно не нравился такой подход.

Под катом свой метод решения задачи, вылившийся в небольшую библиотеку, доступную для общего пользования.

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

Тем не менее есть несколько правил, о которых слдедует помнить. Регистрация built-in декораторов не будет работать. То есть, к сожалению, не получится трэкать такие декораторы, как @staticmethod, @classmethod и им подобные (если кто-то сможет найти решение этой проблемы — буду премного благодарен). И самое главное, — декораторы должны быть зарегистрированы до их использования.

Фактически, механика работы реестра достаточно проста. Регистрируя декоратор вы фактически получаете задекорированый исходный декоратор который помимо своей исходной функциональности также записывает информацию о себе в аттрубут "__annotations__" декорируемой функции.

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

@decorator_one
@decorator_two
@decorator_three
def some_function():
    pass


будет успешно работать.

Библиотека «regd» (так я ее назвал), совместима как с Python 2.x, так и с версией 3.x (на наших проектах у нас используются обе ветки, поэтому совместимость проверялась).

Исходники доступны на Github, лицензия, как всегда — MIT, так что делайте все, что захотите.

Документация здесь.

Установить можно просто через PyPI:

$ pip install regd


Ниже несколько слов о функционале.

1. Регистрация декораторов.

«Обычные» и «параметризованые» декораторы должны регистрироваться разными методами:

from regd import DecoratorRegistry as dreg

# регистрация "обычного" декоратора
simple_decorator = dreg.decorator( mydecorator)

# регистрация "параметризованного" декоратора
param_decorator = dreg.parametrized_decorator( param_decorator)


2. API рефлексии

Чтобы функции API были более понятными давайте для начала создадим и зарегистрируем простой декоратор, который по факту ничнего не делает, а просто существует.

from regd import DecoratorRegistry as dreg

# создадим декоратор
def mydecorator( fn) :
    # здесь может быть какая-то полезная работа...
    def wrapper( *args, **kwargs)
        # ... или здесь что-то полезное ...
        return fn( *args, **kwargs)
    return wrapper

# зарегистрируем наш декоратор
mydecorator = dreg.decorator( mydecorator)


Помните — зарегистрировать декораторы нужно до их использования.

Теперь, после регистрации, можем использовать наш декоратор как обычно:

@mydecorator
def myfunc() :
    pass


Теперь в любой момент из любого места в коде можем узнать, задекорирована ли функция декоратором:
print( dreg.is_decorated_with( myfunc, mydecorator))


Еще несколько полезных методов:

  • all_decorated_module_functions( module, exclude_methods=False, exclude_functions=False) — позволяет получить все функции и/или методы классов задекорированные зарегистрированными декораторами в заданном модуле
  • module_functions_decorated_with( module, decorator, exclude_methods=False, exclude_functions=False) — позволяет получить все функции и/или методы классов задекорированные заданным декоратором в заданном модуле
  • decorated_methods( cls, decorator) — получаем все методы класса/объекта задекорированные заданным декоратором
  • get_decorators( fn) — вернет список все известных декораторов для заданной функции/метода
  • get_real_function( fn) — вернет ссылку ра исходную функцию, которая была задекорирована декораторами (да, можно получить доступ к исходной функции и даже выполнить ее в обход декорирования)
  • is_decorated_with( fn, decorator) — проверяет, задекорирована ли заданная функция заданным декоратором


Надеюсь, кому-то пригодится или покажется полезным. Все замечания и предложения приветствуются.
Теги:
Хабы:
Всего голосов 13: ↑7 и ↓6+1
Комментарии35

Публикации

Истории

Работа

Data Scientist
97 вакансий
Python разработчик
198 вакансий

Ближайшие события

19 августа – 20 октября
RuCode.Финал. Чемпионат по алгоритмическому программированию и ИИ
МоскваНижний НовгородЕкатеринбургСтавропольНовосибрискКалининградПермьВладивостокЧитаКраснорскТомскИжевскПетрозаводскКазаньКурскТюменьВолгоградУфаМурманскБишкекСочиУльяновскСаратовИркутскДолгопрудныйОнлайн
3 – 18 октября
Kokoc Hackathon 2024
Онлайн
10 – 11 октября
HR IT & Team Lead конференция «Битва за IT-таланты»
МоскваОнлайн
25 октября
Конференция по росту продуктов EGC’24
МоскваОнлайн
7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн
7 – 8 ноября
Конференция «Матемаркетинг»
МоскваОнлайн