python从具有相同方法名称的不同路径的多重继承

say*_*yap 23 python multiple-inheritance

下面的代码示例,可以super使用,或者C有来电A.fooB.foo明确?

class A(object):
    def foo(self):
        print 'A.foo()'

class B(object):
    def foo(self):
        print 'B.foo()'

class C(A, B):
    def foo(self):
        print 'C.foo()'
        A.foo(self)
        B.foo(self)
Run Code Online (Sandbox Code Playgroud)

Dun*_*can 23

super确实是出于这种情况,但它只有在你持续使用它时才有效.如果基类也没有全部使用super它将无法工作,除非方法在object你必须使用类似公共基类的东西来终止super调用链.

class FooBase(object):
    def foo(self): pass

class A(FooBase):
    def foo(self):
        super(A, self).foo()
        print 'A.foo()'

class B(FooBase):
    def foo(self):
        super(B, self).foo()
        print 'B.foo()'

class C(A, B):
    def foo(self):
        super(C, self).foo()
        print 'C.foo()'
Run Code Online (Sandbox Code Playgroud)

@Marcin问为什么必须有一个共同的基础:

如果没有FooBase实现,foo但是没有调用super()最后一个调用的类super()将获得属性错误,因为没有基本方法可以调用.

如果有单独的基类class A(AFooBase):,class B(BFooBase):并且super()调用A将调用该方法AFooBase,则B永远不会调用方法.当基类对于所有类都是通用的时,它会转到方法解析顺序的末尾,并且您可以确定无论如何定义类,基类方法都将是最后一个调用的方法.


Ray*_*Luo 18

感谢所有为此主题做出贡献的人.

总结一下:

  • (当前)接受的答案是不准确的.正确的描述应该是:super()不仅对解析单继承有好处,而且还有多重继承.其原因在@blckknght的评论中得到了很好的解释:

    虽然显式调用基类方法可以用于非常简单的场景,例如提问者的示例,但如果基类本身从公共库继承并且您不希望最终基类的方法被调用两次,它将会崩溃.这被称为"钻石继承",对于许多多继承系统来说这是一个大问题(比如在C++中).Python的协作多重继承(使用super())可以让你在很多情况下轻松解决它(虽然这并不是说合作的多继承层次结构很容易设计或总是一个好主意).

  • 正如@duncan指出的那样,正确的方法是使用super(),但要始终如一地使用它.

    super确实是出于这种情况,但它只有在你持续使用它时才有效.如果基类也没有全部使用super它将无法工作,除非方法在object你必须使用类似公共基类的东西来终止super调用链.

    class FooBase(object):
        def foo(self): pass
    
    class A(FooBase):
        def foo(self):
            super(A, self).foo()
            print 'A.foo()'
    
    class B(FooBase):
        def foo(self):
            super(B, self).foo()
            print 'B.foo()'
    
    class C(A, B):
        def foo(self):
            super(C, self).foo()
            print 'C.foo()'
    
    C().foo()  # Run this
    
    Run Code Online (Sandbox Code Playgroud)

    但是值得指出的是,调用顺序的方法在初看时可能看起来并不直观.结果是:

    B.foo()
    A.foo()
    C.foo()
    
    Run Code Online (Sandbox Code Playgroud)

    这看似奇怪的顺序实际的通话顺序仍然C, A, B是基于MRO的.换一种说法,

    super()将在"第一个" "下一个"超级类上调用foo方法.这基于类的方法解析Order(__mro__)C.

    - 引用和修改@ Manoj-Govindan的回答

    >>> C.__mro__
    (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>)
    >>> 
    
    Run Code Online (Sandbox Code Playgroud)
  • 根据经验,如果您想要返回所有父方法但不关心调用顺序,请谨慎使用super().否则,您可以选择以特定顺序显式调用父方法.

  • 不要混合使用super()和显式调用.否则你会得到像这个答案中提到的令人讨厌的重复.

更新:如果你想深入潜水......

简而言之,super(...)在整个班级中使用一致性将确保按照MRO的顺序从祖先调用一次所有同名方法.如果方法碰巧是a ,这样的call-ALL-ancestors(而不是call-only-first-candidate)行为可能在概念上更容易接受,参见本博文中的示例.__init__()

然而,说"遵循MRO命令"可能不是很精确.它实际上始终遵循"孙子"的MRO,不知何故.看到它的实际效果令人着迷.以下程序的结果可能与您的想法不完全相同.注意如何A.__mro__保持在2个不同的调用堆栈相同,但如何super(A, self).namesuper(A, self).foo()何时被触发不同的行为A().foo()和通过C().foo().查看最后引用的结果.

class FooBase(object):
    name = "FooBase"
    def foo(self):
        print('         Base.foo() begins')
        print("         My name is: %s" % self.name)
        print("         My super's name is not available")
        print('         Base.foo() ends')

class A(FooBase):
    name = "A"
    def foo(self):
        print('     A.foo() begins')
        print("     My name is: %s" % self.name)
        print("     My super's name is: %s" % super(A, self).name)
        print("     A.__mro__ is %s" % str(A.__mro__))
        super(A, self).foo()
        print('     A.foo() ends')

class B(FooBase):
    name = "B"
    def foo(self):
        print('     B.foo() begins')
        print("     My name is: %s" % self.name)
        print("     My super's name is: %s" % super(B, self).name)
        print("     B.__mro__ is %s" % str(B.__mro__))
        super(B, self).foo()
        print('     B.foo() ends')

class C(A, B):
    name = "C"
    def foo(self):
        print 'C.foo() begins'
        print("My name is: %s" % self.name)
        print("My super's name is: %s" % super(C, self).name)
        print(" C.__mro__ is %s" % str(C.__mro__))
        super(C, self).foo()
        print('C.foo() ends')


print("We will call A.foo()")
A().foo()

print("We will call C.foo()")
C().foo()  # Run this to see how each foo() is called ONLY ONCE
Run Code Online (Sandbox Code Playgroud)

它的Python 2.7.12的结果是:

We will call A.foo()
     A.foo() begins
     My name is: A
     My super's name is: FooBase
     A.__mro__ is (<class '__main__.A'>, <class '__main__.FooBase'>, <type 'object'>)
         Base.foo() begins
         My name is: A
         My super's name is not available
         Base.foo() ends
     A.foo() ends
We will call C.foo()
C.foo() begins
My name is: C
My super's name is: A
 C.__mro__ is (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.FooBase'>, <type 'object'>)
     A.foo() begins
     My name is: C
     My super's name is: B
     A.__mro__ is (<class '__main__.A'>, <class '__main__.FooBase'>, <type 'object'>)
     B.foo() begins
     My name is: C
     My super's name is: FooBase
     B.__mro__ is (<class '__main__.B'>, <class '__main__.FooBase'>, <type 'object'>)
         Base.foo() begins
         My name is: C
         My super's name is not available
         Base.foo() ends
     B.foo() ends
     A.foo() ends
C.foo() ends
Run Code Online (Sandbox Code Playgroud)

  • 实际上这里的方法调用顺序是`C`、`A`、`B`。它只显示为“B”、“A”、“C”,因为每个方法首先链接到下一个方法,并且仅在另一个方法返回时打印自己的字符串。如果将“print”语句放在“super”调用之前,那么顺序就变得不那么令人惊讶了。或者在之前和之后放置打印件。 (2认同)

Amb*_*ber 10

super()只会解析给定方法的单个类类型,因此如果您从多个类继承并想要在两个类中调用该方法,则需要明确地执行此操作.A.foo(self)

  • 虽然显式调用基类方法*可以用于非常简单的场景,例如提问者的示例,但如果基类本身从公共基础继承并且您不希望最终基类的方法被调用两次,它将会分解.这就是所谓的"菱形继承",这是许多多重继承系统的一个大问题(如在C++).Python的协作多重继承(使用`super()`)可以让你在很多情况下轻松解决它(虽然这并不是说合作的多继承层次结构很容易设计或总是一个好主意). (4认同)

Man*_*dan 9

Super将foo在"第一"超类上调用该方法.这基于类的方法解析Order(__mro__)C.

>>> C.__mro__
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>)
>>> 
Run Code Online (Sandbox Code Playgroud)

因此,如果你打电话super(C, self).foo(),A.foo就叫.如果您将继承顺序更改为class C(B, A):然后这是相反的.在__mro__现在看起来像:

>>> C.__mro__
(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <type 'object'>)
>>> 
Run Code Online (Sandbox Code Playgroud)

如果您super(C, self).foo()在进行此更改后致电,B.foo()则会被致电.


pyf*_*unc 5

如果添加以下内容:

class A(object):
    def foo(self):
        print 'A.foo()'

class B(object):
    def foo(self):
        print 'B.foo()'

class C(A, B):
    def foo(self):
        super(C, self).foo()
        print 'C.foo()'
        A.foo(self)
        B.foo(self)


c = C()
c.foo()
Run Code Online (Sandbox Code Playgroud)

然后super(C,self).foo()指的是A.foo

输出是

A.foo()
C.foo()
A.foo()
B.foo()
Run Code Online (Sandbox Code Playgroud)

[编辑:包含其他信息和链接]