2009-12-07 1 views

ответ

4

Это не работа для декораторов. Вы хотите полностью изменить нормальное поведение класса, поэтому на самом деле это задание для метакласса.

import types 

class CallAll(type): 
    """ MetaClass that adds methods to call all superclass implementations """ 
    def __new__(meta, clsname, bases, attrs): 
     ## collect a list of functions defined on superclasses 
     funcs = {} 
     for base in bases: 
      for name, val in vars(base).iteritems(): 
       if type(val) is types.FunctionType: 
        if name in funcs: 
         funcs[name].append(val) 
        else: 
         funcs[name] = [val] 

     ## now we have all methods, so decorate each of them 
     for name in funcs: 
      def caller(self, *args,**kwargs): 
       """ calls all baseclass implementations """ 
       for func in funcs[name]: 
        func(self, *args,**kwargs) 
      attrs[name] = caller 

     return type.__new__(meta, clsname, bases, attrs) 

class B: 
    def fn(self): 
     print 'B' 

class A: 
    def fn(self): 
     print 'A' 

class C(A,B, object): 
    __metaclass__=CallAll 

c=C() 
c.fn() 
+0

Я действительно предпочитаю решение THC4k лучше, чем мое. : D С квалификацией OP, что он предпочел бы избегать 'super()', похоже, это лучше подходит. Однако, AFAIK, метаклассы работают только с классами нового стиля. +1 к обоим никоу и чувствам экстранеона. –

1

лично я не стал бы делать это с декоратором, так как с помощью новых классов и super(), следующее может быть достигнуто:

>>> class A(object): 
...  def __init__(self): 
...   super(A, self).__init__() 
...   print "A" 
... 
>>> class B(object): 
...  def __init__(self): 
...   super(B, self).__init__() 
...   print "B" 
... 
>>> class C(A, B): 
...  def __init__(self): 
...   super(C, self).__init__() 
... 
>>> foo = C() 
B 
A 

Я бы себе вызовы методов будет работать таким же образом.

+0

Однако здесь мне все равно придется определять каждую функцию в производном классе. Они будут однолинейными функциями, такими как def fn (self): super (C, self) .fn(), но мне все равно придется писать его для каждой функции. То, что я ищу, - это способ избежать этого. Кроме того, для этого мне пришлось бы сделать каждый класс классом нового стиля. – Nikwin

+0

В любом случае вы должны использовать новые классы стиля. Супер - это путь. – nikow

+0

Наследование @Nikwin Constructor исключено из python по дизайну, поэтому, если вы хотите использовать логику конструктора в производных классах, да, вы должны явно вызвать конструкторы. Явное имя игры python, и я не могу сказать, что я нахожу, что Bad Thing :) – extraneon

1

Метакласс - возможное решение, но несколько сложное. super может сделать это очень просто (с новыми классами стилей конечно: нет никаких оснований для использования устаревших классов в новом коде!):

class B(object): 
    def fn(self): 
     print 'B' 
     try: super(B, self).fn() 
     except AttributeError: pass 

class A(object): 
    def fn(self): 
     print 'A' 
     try: super(A, self).fn() 
     except AttributeError: pass 

class C(A, B): pass 

c = C() 
c.fn() 

Вам потребуется попробовать/за исключением поддержки какого-либо порядка одного или множественного наследования (так как в какой-то момент не будет базы по методу-разрешению-порядку, MRO, определяющему метод с именем fn, вам нужно поймать и проигнорировать полученный AttributeError). Но, как вы видите, в отличие от того, о чем вы думаете, основываясь на вашем комментарии на другом ответе, вам необязательно переопределять fn в вашем личном классе, если вам не нужно делать что-то конкретное для этого класса в таком переопределении - super отлично работает на чисто унаследованных (не переопределенных) методах!