2016-02-09 5 views
1

Я пишу универсальный декоратор класса, который должен применять декоратор к каждому методу. Мой первый подход примерно такой:Как украсить класс или статические методы

def class_decorator(cls): 
    for name, member in vars(cls).items(): 
     # Ignore anything that is not a method 
     if not isinstance(member, (types.FunctionType, types.BuiltinFunctionType, classmethod, staticmethod)): 
      continue 

     setattr(cls, name, method_decorator(member)) 

    return cls 

Декоратор сам по себе не очень важен. Выглядит примерно так:

def method_decorator(fn): 
    @functools.wraps(fn) 
    def wrapper(*args, **kwargs): 
     # do something 
     return fn(*args, **kwargs): 

    return wrapper 

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

AttributeError: 'classmethod' object has no attribute '__module__' 

Да , classmethod или staticmethods не являются нормальными функциями, даже не вызываемыми. Как правило, если вам нужно украсить classmethod, вы сначала применяете свой декоратор, а затем декоратор classmethod, но поскольку это декоратор класса, я не могу повлиять на порядок декораторов.

Любое хорошее решение для этого?

+0

потому, что декоратор создает двусмысленность: метод класса, чтобы связать объект не является объектом-функцией (что возвращается декоратор), поэтому он не может связать метод с класс –

ответ

3

Поиграв некоторое время, я нашел решение, которое выглядит лучше, чем другие подходы в SO. Может быть, это может быть полезно кому-то.

В принципе идея заключается в следующем:

  • Обнаружить пользователей, которые класс или статические методы
  • Получить функциональный объект, обернутый в пределах этих методов
  • Применить декоратор этой функции
  • Оберните оформленная функция в пределах classmethod или staticmethod пример
  • Сохраните его в классе снова

код выглядит следующим образом:

def class_decorator(cls): 
    for name, member in vars(cls).items(): 
     # Good old function object, just decorate it 
     if isinstance(member, (types.FunctionType, types.BuiltinFunctionType)): 
      setattr(cls, name, method_decorator(member)) 
      continue 

     # Static and class methods: do the dark magic 
     if isinstance(member, (classmethod, staticmethod)): 
      inner_func = member.__func__ 
      method_type = type(member) 
      decorated = method_type(method_decorator(inner_func)) 
      setattr(cls, name, decorated) 
      continue 

     # We don't care about anything else 

    return cls