我在python中有一个类'D'的对象,我想按顺序执行'D'定义的'run'方法及其每个祖先('A','B'和'C')
我能够像这样完成这个
class A(object):
def run_all(self):
# I prefer to execute in revere MRO order
for cls in reversed(self.__class__.__mro__):
if hasattr(cls, 'run'):
# This works
cls.run(self)
# This doesn't
#cls.__getattribute__(self, 'run')()
def run(self):
print "Running A"
class B(A):
def run(self):
print "Running B"
class C(A):
def run(self):
print "Running C"
class D(C, B):
def run(self):
print "Running D"
if __name__ == "__main__":
D().run_all()
结果是
$ python test.py
Running A
Running B
Running C
Running D
但实际上我不知道要执行的方法的名称。但是,如果我使用 getattribute ()(请参阅注释)行尝试此操作它不起作用:
$ python test.py
Running D
Running D
Running D
Running D
所以我的问题是:
为什么不工作?
这是最好的解决方法吗?
答案 0 :(得分:4)
如果您可以更改所有run
实施(并在D中调用run
而不是run_all
),则可行:
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
更改为:
run
在A
和C
中替换为Running A
Running C
的两个不同in
的最新示例,使示例打印:
None
我怀疑它可能更接近你想要的东西。
还有一点要点:不要重复工作 - 监视getattr,或者getattr
测试保护dict访问 - 守卫检查和守卫访问者必须重复完全相同在内部工作,没有好的目的。相反,使用get
的第三个参数到单个None
调用(或dict的get
方法):这意味着如果方法不存在,您将检索{{{ 1}}值,然后您可以针对该事件保护调用。这正是dicts具有getattr
方法和{{1}}具有第三个可选“默认”参数的原因:为了便于应用DRY,“不要重复自己”,这是一个非常重要的格言好的节目! - )
答案 1 :(得分:1)
您不应该使用__getattribute__
方法..
只需执行以下操作:
getattr(cls, 'run')(self)
答案 2 :(得分:1)
为什么不简单地使用super
?虽然有些consider it harmful,但它的设计考虑到了这种情况,我会毫不犹豫地使用它。
这对于访问继承有用 已被覆盖的方法 类。搜索顺序与 getattr()使用的除了 类型本身被跳过。 [...] 这个 使实现成为可能 “钻石图”里面有多个基础 类实现相同的方法。
更新:在您的情况下,它会变成这样:
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()