2013-11-26 3 views
2

Может кто-нибудь объяснить, почему следующий код не работает? Я пытаюсь создать декоратор класса, чтобы предоставить новые методы __repr__ и __init__, и если я украшу его классом, то будет определен метод rep. Мне удалось исправить исходную проблему, заставив декоратора модифицировать оригинальный класс деструктивно вместо создания нового класса (например, он определяет новые методы, а затем просто использует cl.__init__ = __init__ для их перезаписи). Теперь мне просто интересно, почему попытка на основе подкласса не сработала.Классы более высокого порядка в Python

def higherorderclass(cl): 
    @functools.wraps(cl) 
    class wrapped(cl): 
     def __init__(self, *args, **kwds): 
      print 'in wrapped init' 
      super(wrapped, self).__init__(*args, **kwds) 
     def __repr__(self): 
      return 'in wrapped repr' 
    return wrapped 
+1

для стартеров, '@ functools.wraps()' относится к * Функция декораторы * только , –

ответ

4

Первая проблема заключается в том, что вы используете классы старого стиля. (То есть классы, которые не наследуются от object, другого встроенного типа или другого класса нового стиля.) Специальный поиск методов работает по-разному в классах старого стиля. Действительно, вы не хотите узнать, как это работает; просто используйте классы нового стиля.

Но тогда вы столкнулись с следующей проблемой: functools.wraps не работает на занятиях в первую очередь. С классами нового стиля вы получите своего рода AttributeError; с классами старого стиля, вещи просто бесшумно терпят неудачу различными способами. И вы не можете просто использовать update_wrapper явно. Проблема в том, что вы пытаетесь заменить атрибуты класса, которые не могут быть записаны, и нет (прямого) способа обойти это.

Если вы используете классы нового стиля и не пытаетесь использовать их wraps, все работает нормально.

+0

Как вы можете сказать, что вход старый? – user2357112

+0

Спасибо! Есть ли что-то, что я должен изменить вручную, чтобы обеспечить подобное интроспективное поведение, например functools.wraps, или достаточно подклассифицировать? –

+0

@ user2357112: Я мог сказать, потому что мне довелось узнать (и я не уверен, почему), что иначе OP получил бы исключение «AttributeError» и не задал бы этот вопрос. (Было бы лучше, если бы он действительно показал нам свой класс, но ему повезло.) – abarnert

3

Извлеките декоратор @functools.wraps(), это относится только к декораторам функций. С Newstyle класса ваш декоратор терпит неудачу с:

>>> @higherorderclass 
... class Foo(object): 
...  def __init__(self): 
...   print 'in foo init' 
... 
Traceback (most recent call last): 
    File "<stdin>", line 2, in <module> 
    File "<stdin>", line 3, in higherorderclass 
    File "/Users/mj/Development/Library/buildout.python/parts/opt/lib/python2.7/functools.py", line 33, in update_wrapper 
    setattr(wrapper, attr, getattr(wrapped, attr)) 
AttributeError: attribute '__doc__' of 'type' objects is not writable 

Без @functools.wraps() линии ваш декоратор работает просто отлично:

>>> def higherorderclass(cl): 
...  class wrapped(cl): 
...   def __init__(self, *args, **kwds): 
...    print 'in wrapped init' 
...    super(wrapped, self).__init__(*args, **kwds) 
...   def __repr__(self): 
...    return 'in wrapped repr' 
...  return wrapped 
... 
>>> @higherorderclass 
... class Foo(object): 
...  def __init__(self): 
...   print 'in foo init' 
... 
>>> Foo() 
in wrapped init 
in foo init 
in wrapped repr