【问题标题】:Python Call Parent Method Multiple InheritancePython调用父方法多重继承
【发布时间】:2013-12-25 09:34:14
【问题描述】:

所以,我有这样的情况。

class A(object):
    def foo(self, call_from):
        print "foo from A, call from %s" % call_from


class B(object):
    def foo(self, call_from):
        print "foo from B, call from %s" % call_from


class C(object):
    def foo(self, call_from):
        print "foo from C, call from %s" % call_from


class D(A, B, C):
    def foo(self):
        print "foo from D"
        super(D, self).foo("D")

d = D()
d.foo()

代码的结果是

foo from D
foo from A, call from D

我想从D 类调用所有父方法,在本例中为foo 方法,而不像A 那样在父类中使用super。我只想从D 类中调用超级。 ABC 类就像 mixin 类,我想从 D 调用所有 foo 方法。我怎样才能做到这一点?

【问题讨论】:

标签: python class inheritance multiple-inheritance


【解决方案1】:

在除C 之外的其他类中添加super() 调用。由于 D 的 MRO 是

>>> D.__mro__
(<class '__main__.D'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <type 'object'>)

C 不需要超级呼叫。

代码:

class A(object):
    def foo(self, call_from):
        print "foo from A, call from %s" % call_from
        super(A,self).foo('A')

class B(object):
    def foo(self, call_from):
        print "foo from B, call from %s" % call_from
        super(B, self).foo('B')


class C(object):
    def foo(self, call_from):
        print "foo from C, call from %s" % call_from

class D(A, B, C):
    def foo(self):
        print "foo from D"
        super(D, self).foo("D")

d = D()
d.foo()

输出:

foo from D
foo from A, call from D
foo from B, call from A
foo from C, call from B

【讨论】:

  • 如果你不明白我的描述,我想调用所有的foo方法,而不是像ABC这样的父类调用super。
  • @EdwinLunando -- 我相信事情并非如此。为了让 super 正常工作,所有的类都需要使用它。 see the section on mixing super methods with non super methods.
  • @EdwinLunando 然后遍历__mro____bases__ 并显式调用每个类,但是你不能在D 中使用 super
  • 这个解决方案是错误的,因为现在调用A().foo()B().foo() 会引发异常。您现在只能使用派生类中的这些类,该派生类也派生自C(或类似的类),并且顺序也很重要(C 必须在最后)。为了使这项工作正常工作,您需要从实现foo 的基类派生所有类。
【解决方案2】:

你可以像这样使用__bases__

class D(A, B, C):
    def foo(self):
        print("foo from D")
        for cls in D.__bases__:
            cls().foo("D")

有了这个改变,输出将是

foo from D
foo from A, call from D
foo from B, call from D
foo from C, call from D

【讨论】:

  • 这有点粗略......如果你现在有两个继承自 D 的类和另一个继承自这两者的类怎么办?现在foo 将调用A.foo 两次。
  • 或者如果B.foo 不存在会怎样?最后,我认为应该是cls.foo(self),而不是cls().foo()
  • @mgilson 那么,我们必须使用mro?那些不是类方法,对吧?所以,我必须创建对象来调用它们。
  • 它们不是类方法,但如果您明确将self 作为第一个参数传递(并且self 的类型正确),则可以从类中调用该方法。
  • 在这种情况下,self 没有问题,但在他的实际代码中,他可能会变异 self,这会产生问题。所以,我认为最好这样称呼它
【解决方案3】:

我相信在子类中调用 super 是更 Pythonic 的方法。不必使用父类名称(尤其是在 super 中)。详细说明前面的示例,这里有一些应该可以工作的代码(python 3.6+):

class A:
    def foo(self, call_from):
        print(f"foo from A, call from {call_from}")
        super().foo('A')

class B:
    def foo(self, call_from):
        print(f"foo from B, call from {call_from}")
        super().foo('B')


class C(object):
    def foo(self, call_from):
        print(f"foo from C, call from {call_from}")
        super().foo('C')

class StopFoo:
    def foo(self, call_from):
        pass

class D(A, B, C, StopFoo):
    def foo(self, call_from):
        print(f"foo from D, call from {call_from}")
        super().foo('D')

如果你运行这段代码:

d = D()
d.foo('D')

你会得到:

foo from D, call from D
foo from A, call from D
foo from B, call from A
foo from C, call from B

这种策略的优点是您不必担心继承顺序,只要包含StopFoo 类即可。这个有点特殊,可能不是完成这项任务的最佳策略。基本上,继承树中的每个类都会调用foo 方法并调用父方法,它们的作用相同。我们可能在谈论多重继承,但继承树实际上是扁平的(D -> A -> B -> C -> Stopfoo -> object)。我们可以更改继承顺序,向此模式添加新类,删除它们,只需调用一个类......但技巧仍然存在:在调用 foo 离开我们定义的类之前包含 StopFoo

对于带有钩子的混合模式,这可能是有意义的。但是,当然,解决方案也不是在每种情况下都那么有用。不过不要害怕super,它有很多技巧,可以用于简单和多重继承,使用mixin 或简单抽象类。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-14
    • 1970-01-01
    • 2021-08-02
    • 1970-01-01
    • 2012-08-29
    • 1970-01-01
    相关资源
    最近更新 更多