Si vous êtes OK avec changement de toutes les implémentations run
(et appelant run
au lieu de run_all
en D), cela fonctionne:
class A(object):
def run(self):
print "Running A"
class B(A):
def run(self):
super(B, self).run()
print "Running B"
class C(A):
def run(self):
super(C, self).run()
print "Running C"
class D(C, B):
def run(self):
super(D, self).run()
print "Running D"
if __name__ == "__main__":
D().run()
Notez que je ne suis pas utilisation super
dans la classe racine - il « sait » il n'y a pas superclasse plus aller jusqu'à (object
ne définit pas une méthode run
). Malheureusement, dans Python 2, c'est inévitablement verbeux (et pas bien adapté à l'implémentation via un décorateur, soit).
Votre chèque sur hasattr
est assez fragile, si je comprends bien vos objectifs - il trouvera qu'une classe « a » l'attribut si elle définit ou hérite il. Donc, si vous avez une classe intermédiaire qui ne remplace pas run
mais se produit sur le __mro__
, la version de run
héritée est appelée deux fois dans votre approche. Par exemple., Tenez compte:
class A(object):
def run_all(self):
for cls in reversed(self.__class__.__mro__):
if hasattr(cls, 'run'):
getattr(cls, 'run')(self)
def run(self):
print "Running A"
class B(A): pass
class C(A):
def run(self):
print "Running C"
class D(C, B): pass
if __name__ == "__main__":
D().run_all()
cette imprime
Running A
Running A
Running C
Running C
avec deux "bégaiements" pour les versions de run
qui B
et D
Hériter sans écraser (de A
et C
respectivement). En supposant que je ne me trompe pas que ce soit pas l'effet que vous voulez, si vous êtes désireux d'éviter super
vous pouvez essayer de changer run_all
à:
def run_all(self):
for cls in reversed(self.__class__.__mro__):
meth = cls.__dict__.get('run')
if meth is not None: meth(self)
qui, substitué dans mon dernier exemple avec seulement deux def
distincts s pour run
dans A
et C
, fait l'impression par exemple:
Running A
Running C
que je pense peut-être plus proche de ce que vous voulez. Un autre côté: ne répétez pas le travail - getattr guarding getattr, ou un in
test gardant l'accès dict - à la fois la vérification dans la garde, et l'accesseur gardé, doit répéter exactement le même travail en interne, pas de bon but. Au lieu de cela, utilisez un troisième argument de None
pour un seul appel getattr
(ou la méthode get
du dict): cela signifie que si la méthode est absente, vous récupérerez une valeur None
, et vous pourrez alors garder l'appel contre cela occurrence. C'est exactement la raison pour laquelle les dicts ont une méthode get
et getattr
a un troisième argument "default" optionnel: pour faciliter l'application de DRY, "ne vous répétez pas", une maxime très importante de bonne programmation! -)
vous pouvez utiliser __getattribute__, mais il aura l'air très "pas sympa" - cls .__ getattribute __ (cls, 'run') (auto) –