2009-12-07 12 views

Répondre

4

Ce n'est pas un travail pour les décorateurs. Vous voulez changer complètement le comportement normal d'une classe, c'est donc un travail pour une métaclasse.

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

En fait, j'aime mieux la solution de THC4k que la mienne. : D Avec la qualification de l'OP qu'il préfère éviter 'super()', cela semble mieux correspondre. Cependant, AFAIK, les métaclasses ne fonctionnent qu'avec des classes de style nouveau. +1 aux sentiments de nikow et d'extraneon, cependant. –

1

Personnellement, je ne pas essayer de faire cela avec un décorateur puisque l'utilisation de nouvelles classes de style et super(), les éléments suivants peuvent être obtenus:

>>> 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 

J'imagine invocations de méthode fonctionnerait de la même manière.

+0

Cependant, ici je devrais encore définir chaque fonction dans la classe dérivée. Ils seraient des fonctions à une seule ligne, comme def fn (self): super (C, self) .fn(), mais je devrais quand même l'écrire pour chaque fonction. Ce que je cherche est un moyen d'éviter cela. Aussi, pour cela, je devrais faire de chaque classe une nouvelle classe. – Nikwin

+0

De toute façon, vous devriez utiliser de nouvelles classes de style. Super est le chemin à parcourir. – nikow

+0

L'héritage @Nikwin Constructor est laissé hors de python par conception, donc si vous voulez utiliser la logique constructeur dans les classes dérivées, oui, vous devez appeler les constructeurs explicitement. Explicit est le nom du jeu python, et je ne peux pas dire que je trouve qu'une mauvaise chose :) – extraneon

1

Une métaclasse est une solution possible, mais quelque peu complexe. super peut le faire très simplement (avec de nouvelles classes de style bien sûr: il n'y a aucune raison d'utiliser des classes existantes dans le nouveau code!): Vous avez besoin du try/sauf

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() 

soutenir un ordre d'héritage unique ou multiple (puisque à un certain point, il n'y aura plus de base le long de l'ordre de résolution de la méthode, MRO, en définissant une méthode nommée fn, vous devez attraper et ignorer le résultat AttributeError). Mais comme vous le voyez, différemment de ce que vous semblez penser en fonction de votre commentaire à une réponse différente, vous n'avez pas nécessairement besoin de surcharger fn dans votre classe leafmost sauf si vous avez besoin de faire quelque chose de spécifique à cette classe dans un tel override - super fonctionne bien sur les méthodes purement héritées (pas surchargées), aussi!