4

Я хотел бы использовать декоратор, чтобы что-то сделать с производным классом (например, зарегистрировать класс или что-то еще). Вот мой код:Как управлять декоратором на производном классе python?

from functools import wraps 

class Basic(object): 
    def __init__(self): 
     print "Basic::init" 

def myDeco(name): 
    # define the decorator function that acts on the actual class definition 
    def __decorator(myclass): 

     # do something here with the information 
     print name, myclass 

     # do the wrapping of the class 
     @wraps(myclass) 
     def __wrapper(*args, **kwargs): 
      return myclass(*args, **kwargs) 

     # return the actual wrapper here 
     return __wrapper 

    # return the decorator to act on the class definition 
    return __decorator 

@myDeco("test") 
class Derived(Basic): 
    def __init__(self): 
     super(Derived, self).__init__() 
     print "Derived::init" 


instance = Derived() 

, который дает следующее сообщение об ошибке:

TypeError: must be type, not function 

когда метод super в Derived называется. Я предполагаю, что переменная Derived больше не является type, но функция __decorator фактически.

Как мне изменить декоратор (и ТОЛЬКО декоратор), чтобы исправить эту проблему?

+0

декоратор внутри декоратор - он просит проблемы ... –

+1

@JakubM .: Нет, функция фабрика декоратора. Это * обычная практика *. –

+0

'@ wraps' внутри' myDeco'? выглядит странно для меня –

ответ

3

Вы заменяете украшенный класс функцией, и поэтому определение класса не выполняется.

В частности, super(Derived, self).__init__() в настоящее время проходит в функции в super():

>>> super(Derived, object()) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: must be type, not function 
>>> type(Derived) 
<type 'function'> 

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

Вы должны не заменить Derived с новым классом, потому что ваш Derived.__init__() называет себя по имени. Замена этого имени другим классом приведет к боли (например, к бесконечной рекурсии).

Пример класса декоратор, который делает работы:

def my_deco(name): 
    # define the decorator function that acts on the actual class definition 
    def decorator(cls): 

     # do something here with the information 
     print name, cls 

     setattr(class, name, 'Hello world!') 

     # return the original, augmented class 
     return cls 

    # return the decorator to act on the class definition 
    return decorator 
0

Вы были две ошибки в программе:

from functools import wraps 

class Basic(object): 
    def __init__(self): 
     print "Basic::init" 

def myDeco(name): 
    # define the decorator function that acts on the actual class definition 
    def __decorator(myclass): 

     # do something here with the information 
     print name, myclass 

     # do the wrapping of the class 
     class Wrap(myclass): # error 1 
      realSuper = myclass # I dislike this. has anyone an other aproach 
      def __new__(*args, **kwargs): 
       return myclass.__new__(*args, **kwargs) 

     # return the actual wrapper here 
     return Wrap 

    # return the decorator to act on the class definition 
    return __decorator 

@myDeco("test") 
class Derived(Basic): 
    def __init__(self): 
     super(Derived.realSuper, self).__init__() # error 2 
     print "Derived::init" 


instance = Derived() 

Это теперь дает следующий результат:

test <class '__main__.Derived'> 
Basic::init 
Derived::init 

Сначала вам нужен класс, как указывали другие.

Второй super(Derived, self).__init__() вызвал бесконечную рекурсию:

Traceback (most recent call last): 
    File "<pyshell#8>", line 1, in <module> 
    exec s 
    File "<string>", line 32, in <module> 
    File "<string>", line 28, in __init__ 
    File "<string>", line 28, in __init__ 
    File "<string>", line 28, in __init__ 
    File "<string>", line 28, in __init__ 

По причинам см обсуждение ниже.

+0

Нет, 'super (Derived, self)' is * correct *. Python нуждается в * текущем классе * в качестве отправной точки для поиска следующего класса в MRO. Переходя в «Basic», вы явно * пропустите * **, что ** класс в MRO. –

+0

Пожалуйста, объясните, какие ошибки были сделаны, почему они были ошибками и как они были исправлены. Исправленный код сам по себе является плохим ответом. – Allan

+0

@Martijn Pieters, пожалуйста, скажите мне, почему в этой строке бесконечная рекурсия, если я ее не изменю. – User