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

时间:2010-09-28 07:05:13

标签: 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)

9 个答案:

答案 0 :(得分:21)

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

super

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

如果没有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()' 实现FooBase但没有调用foo,那么调用super()的最后一个类将会出现属性错误,因为没有基本方法可以调用

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

答案 1 :(得分:14)

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

总结:

  • The (currently) accepted answer不准确。正确的描述应该是:super()不仅适用于解析单继承,还有多重继承。其原因在@blckknght的评论中得到了很好的解释:

      

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

  • 正如@duncan pointed out一样,正确的方法是使用super(),但要始终如一地使用它。

      

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

    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
    

    这个看似奇怪的顺序实际的通话顺序仍然是B.foo() A.foo() C.foo() ,它基于MRO。换句话说,

      

    C, A, B将在“first”“next”超类上调用foo方法。这基于类super()的方法解析顺序(__mro__)。

         

    - 从@Manoj-Govindan 's answer

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

  • 不要混用super()和显式调用的用法。否则,您将最终在this answer中提到令人讨厌的重复。

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

简而言之,在整个班级中始终如一地使用super()将确保祖先的所有同名方法被调用一次,大约为MRO。如果方法恰好是super(...)call-only-the-first-candidate,那么这种调用ALL-ancestors(而不是see example in this blog post)行为可能在概念上更容易接受。

尽管如此,说“遵循MRO命令”可能并不十分精确。它实际上始终遵循“孙子”的MRO,不知何故。看到它的实际效果令人着迷。以下程序的结果可能与您的想法不完全相同。请注意__init__()在2个不同的调用堆栈中保持相同的方式,A.__mro__super(A, self).namesuper(A, self).foo()A().foo()触发时的行为方式不同。查看最后引用的结果。

C().foo()

Python 2.7.12的结果是:

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

答案 2 :(得分:8)

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

答案 3 :(得分:8)

Super将在“第一个”超类上调用foo方法。这基于类__mro__的方法解析顺序(C)。

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

因此,如果您致电super(C, self).foo(),则会调用A.foo。如果将继承顺序更改为class C(B, A):,那么这是相反的。 __mro__现在看起来像:

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

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

答案 4 :(得分: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()

然后super(C,self).foo()引用A.foo

输出

A.foo()
C.foo()
A.foo()
B.foo()

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

答案 5 :(得分:2)

正如Duncan所说,你可以得到可预测的结果,特别是如果你一直使用super()。

以下测试的结果对我有帮助:

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

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

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

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

这将打印出来:

>>> c = C()
>>> c.foo()
C.foo() before super()
A.foo() before super()
B.foo() before super()
FooBase.foo()
B.foo() after super()
A.foo() after super()
C.foo() after super()

答案 6 :(得分:0)

这可以通过super

实现
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()'
        super(C, self).foo() # calls A.foo()
        super(A, self).foo() # calls B.foo()

答案 7 :(得分:0)

$this->db->select('projectID');
$this->db->from('propertytype');
$this->db->distinct('projectID');
$rec=$this->db->get()->result();
$arr=array();

foreach ($rec as $r) {
    $this->db->select('typeID,projectID,typeName,typeSize,dimensionWidth,dimensionHeight');
    $this->db->from('propertytype');
    $this->db->where('projectID',$r->projectID);
    $rec=$this->db->get()->result();
    $arr[]=$rec;    
}

echo "<pre>";print_r($arr);exit();

输出:

  

C类::

     

A级

答案 8 :(得分:0)

在这里您可以看到如何调用具有相同方法的类:-

这里,在类 D(A,B,C) 的方法 'feat()' 中

super(D, self).feature2() ----> 会调用A类的方法(feature2)。

super(A, self).feature2() ----> 会调用B类的方法(feature2)。

super(B, self).feature2() ---->会调用C类的方法(feature2)。

class A:

    def __init__(self):
        print("in A Init")

    def feature1(self):
        print("Feature 1-A working")

    def feature2(self):
        print("Feature 2-A working")

class B:

    def __init__(self):
        print("in B Init")

    def feature1(self):
        print("Feature 1-B working")

    def feature2(self):
        print("Feature 2-B working")

class C:

    def __init__(self):
        print("in C Init")

    def feature1(self):
        print("Feature 1-C working")

    def feature2(self):
        print("Feature 2-C working")

    

class D(A,B,C):

    def __init__(self):
        super().__init__()
        print("in D init")


    def feat(self):
        super(D, self).feature2()
        super(A, self).feature2()
    
        print('\n\n')

        ### B().feature2() is calling explicitly, but 
        ### super(A, self).feature2() is not.
        
        ### Both will give same result in below,
        ### It will print True below

        print(B().feature2() == super(A, self).feature2())

        print('\n\n')
    
        super(B, self).feature2()
        print(D.__mro__)


a1 = D()
a1.feat()

结果是:-

in A Init
in D init
Feature 2-A working
Feature 2-B working



in B Init
Feature 2-B working
Feature 2-B working
True



Feature 2-C working
(<class '__main__.D'>, <class '__main__.A'>, <class '__main__.B'>, <class 
'__main__.C'>, <class 'object'>)