Если вы ОК с изменением всех run
реализаций (и вызова run
вместо run_all
в D), это работает:
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()
Обратите внимание, что я делать не использовать super
в корневом классе - это «знает», что нет никакого дальнейшего суперкласса идти до (object
не определяет метод run
). К сожалению, в Python 2 это неизбежно многословно (и не очень хорошо подходит для реализации через декоратора).
Ваш чек на hasattr
довольно хрупкий, если я понимаю, ваши цели правильно - он найдет, что класс «имеет» атрибут, если он определяет или наследует его. Поэтому, если у вас есть промежуточный класс, который не переопределяет run
, но имеет место на __mro__
, то версия run
, наследуемая, дважды вызывается в вашем подходе. Например.Рассмотрит:
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()
это печатает
Running A
Running A
Running C
Running C
с двумя "заикается" для версий run
этого B
и D
наследуют без переопределения (от A
и C
соответственно). Предполагая, что я прав, что это не эффекта, который вы хотите, если вы заинтересованы, чтобы избежать super
вы можете попробовать изменить 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)
, который подставляется в мой последний пример только с двумя различными def
s для run
в A
и C
, делает пример печати:
Running A
Running C
, который я подозреваю, может быть ближе к тому, что вы хотите.
Еще одна боковая точка: не повторяйте работу - hasattr guarding getattr или in
контрольно-проверочный доступ к диктофону - как проверка в охраннике, так и охраняемый аксессуар, должны повторять ту же самую работу внутри, к доброй цели. Скорее, используйте третий аргумент None
одному звонку getattr
(или методу get
): это означает, что если этот метод отсутствует, вы получите значение None
, а затем вы можете защитить звонок против этого вхождение. Именно поэтому dicts имеет метод get
, а getattr
имеет третий необязательный аргумент «по умолчанию»: чтобы упростить применение DRY, «не повторяйте себя», очень важный максимум хорошего программирования! -)
вы можете использовать __getattribute__, но он будет выглядеть очень «неплохо» - cls .__ getattribute __ (cls, 'run') (self) –