在Python中对每个祖先调用方法
在python中,我有一个类为“D”的对象,我想按顺序执行由“D”及其每个祖先(“A”、“B”和“C”)定义的“run”方法 我能做到这一点在Python中对每个祖先调用方法,python,datamodel,Python,Datamodel,在python中,我有一个类为“D”的对象,我想按顺序执行由“D”及其每个祖先(“A”、“B”和“C”)定义的“run”方法 我能做到这一点 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'):
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
因此,我的问题是:
您不应该使用
\uuuu getattribute\uuuu
方法
只需执行以下操作:
getattr(cls, 'run')(self)
你为什么不干脆用
super
?虽然有些,但它的设计正是考虑到这种情况,我会毫不犹豫地使用它
发件人:
这对于访问继承的
已在中重写的方法
班级。搜索顺序与相同
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()
如果您可以更改所有的
run
实现(并在D中调用run
而不是run\u 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
方法)。不幸的是,在Python2中,这不可避免地是冗长的(并且也不适合通过decorator实现)
如果我正确理解您的目的,那么您对hasattr的检查是非常脆弱的——如果类定义或继承了该属性,它将发现该类“具有”该属性。因此,如果您有一个中间类,该类不重写run
,但确实发生在\uu mro\uu
上,那么它继承的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\u 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替换A
和C
中的run
,使示例打印:
Running A
Running C
我想这可能更接近你想要的
还有一个侧重点:不要重复这项工作——hasattr-guarding-getattr,或在测试保护dict-access中的
——无论是在保护中签入,还是在保护访问器中,都必须在内部重复完全相同的工作,没有什么好的效果。相反,对单个getattr
调用(或dict的get
方法)使用None
的第三个参数:这意味着如果该方法不存在,您将检索一个None
值,然后您可以防止该调用发生。这正是dicts有一个get
方法和getattr
有第三个可选的“default”参数的原因:为了便于直接应用,“不要重复自己”,这是优秀编程的一条非常重要的准则!) 你可以使用getattribute,但它看起来很“不好”-cls.\uuuuuuGetAttribute\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,super
需要明确的类规范,正如我的答案中的第一个代码建议——使用self.\uuuuu class\uuuuu
,在其中无法正常工作。(顺便说一句,Python3这样要好一点)为什么不直接使用super(D,self).run()?我在主要答案中举了一个例子,在我的例子中,子类将表示可以继承的服务器配置,等等,我试图在python的限制内尽可能保持语法的声明性。因此,我不希望每个子类的每个方法都必须手动调用super。@Roberto,你说“即使在层次结构的“根”类上也需要调用super”是完全错误的!只要试一下你的代码,你就会看到一个带有AttributeError的回溯:“super”对象没有属性“run”
;删除你声称需要的super
调用A.run
,就像我的答案代码省略了它一样,你会看到回溯消失,一切正常。。。当然,这与我的回答完全一样,因为,一旦这个可怕的错误被修复,您的代码就会与我的代码相同;-)。这正是我想要的,你说的“口吃”是对的,我没有想到。谢谢大家!另外,据我所知,要将super与多重继承结合使用,您确实需要从“root”类调用它,以便遍历整个树/菱形/任何东西(正如Roberto在下面所做的),尽管我可能在这一点上错了。