在 Python 中调用每个祖先的方法
我在 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'):
# 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
所以我的问题是:
为什么它不起作用?
这是否是解决此问题的最佳方法?
I have an object of class 'D' in python, and I want to sequentially execute the 'run' method as defined by 'D' and each of it's ancestors ('A', 'B' and 'C').
I'm able to accomplish this like this
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()
Which results in
$ python test.py
Running A
Running B
Running C
Running D
However in practice I won't know the name of the method to be executed. But if I try this using getattribute() (see the commented) line it doesn't work:
$ python test.py
Running D
Running D
Running D
Running D
So my questions are:
Why isn't it working?
Is this even the best way to go about this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
如果您同意更改所有
run
实现(并调用run
而不是 D 中的run_all
),则此方法有效:请注意,我 < em>不要在根类中使用
super
——它“知道”没有进一步的超类可以向上(object
没有定义<代码>运行方法)。不幸的是,在 Python 2 中,这不可避免地很冗长(并且也不太适合通过装饰器实现)。如果我正确理解你的目的,你对 hasattr 的检查非常脆弱——如果一个类定义或继承了它,它会发现它“拥有”该属性。因此,如果您有一个不重写
run
但确实出现在__mro__
上的中间类,则它继承的run
版本会被调用两次在你的方法中。例如,考虑一下:此打印
对于
B
和D
继承而不覆盖的run
版本, 有两个“口吃”(来自A<分别为 /code> 和
C
)。假设我是对的,这不是您想要的效果,如果您热衷于避免super
您可以尝试将run_all
更改为:将其替换为我的最新示例,在
A
和C
中仅用两个不同的def
来表示run
,使得示例打印:我怀疑这可能更接近您想要的。
还有一点:不要重复这项工作——hasattr 保护 getattr,或者
in
测试保护 dict 访问——保护中的检查和受保护的访问器必须重复完全相同内部工作,没有什么好的目的。相反,对单个getattr
调用(或字典的get
方法)使用第三个参数None
:这意味着如果该方法如果不存在,您将检索None
值,然后您可以防止调用发生这种情况。这正是 dicts 有get
方法和getattr
有第三个可选“默认”参数的原因:为了轻松应用 DRY,“不要重复自己” ,良好编程的一个非常重要的格言!-)If you're OK with changing all the
run
implementations (and callingrun
instead ofrun_all
in D), this works:Note that I don't use
super
in the root class -- it "knows" there's no further superclass to go up to (object
does not define arun
method). Unfortunately, in Python 2, this is inevitably verbose (and not well suited to implementing via a decorator, either).Your check on
hasattr
is quite fragile, if I understand your purposes correctly -- it will find that a class "has" the attribute if it defines or inherits it. So if you have an intermediate class that doesn't overriderun
but does occur on the__mro__
, the version ofrun
it inherits gets called twice in your approach. E.g., consider:this prints
with two "stutters" for versions of
run
thatB
andD
inherit without overriding (fromA
andC
respectively). Assuming I'm right that this is not the effect you want, if you're keen to avoidsuper
you could try changingrun_all
to:which, substituted into my latest example with just two distinct
def
s forrun
inA
andC
, makes the example print:which I suspect may be closer to what you want.
One more side point: don't repeat the work -- hasattr guarding getattr, or an
in
test guarding dict access -- both the check in the guard, and the guarded accessor, must repeat exactly the same work internally, to no good purpose. Rather, use a third argument ofNone
to a singlegetattr
call (or theget
method of the dict): this means that if the method is absent you'll retrieve aNone
value, and then you can guard the call against that occurrence. This is exactly the reason dicts have aget
method andgetattr
has a third optional "default" argument: to make it easy to apply DRY, "don't repeat yourself", a very important maxim of good programming!-)您不应该使用
__getattribute__
方法..只需执行以下操作:
You should not use
__getattribute__
method..just do the following:
为什么不直接使用
super
呢?虽然有些认为它有害,但它的设计正是考虑到了这种情况,我会毫不犹豫地使用它。来自 Python 文档:
更新:在你的情况下,它会变成这样:
Why don't you simply use
super
? Although some consider it harmful, it was designed with exactly this kind of scenario in mind, and I would use it without any hesitation.From Python documentation:
Update: In your case, it would become something like this: