有什么区别:

class Child(SomeBaseClass):
    def __init__(self):
        super(Child, self).__init__()

and:

class Child(SomeBaseClass):
    def __init__(self):
        SomeBaseClass.__init__(self)

我看到super在只有单一继承的类中被大量使用。我可以理解为什么在多重继承中使用它,但不清楚在这种情况下使用它的优势是什么。


当前回答

class Child(SomeBaseClass):
    def __init__(self):
        SomeBaseClass.__init__(self)

这很容易理解。

class Child(SomeBaseClass):
    def __init__(self):
        super(Child, self).__init__()

如果你使用super(Child,self)会发生什么?

当创建子实例时,它的MRO(方法解析顺序)是基于继承的(Child, SomeBaseClass, object)顺序。(假设SomeBaseClass除了默认对象之外没有其他父对象)

通过在self实例的MRO中传递Child、self、super搜索,并返回Child的next代理对象,在本例中是SomeBaseClass,该对象随后调用SomeBaseClass的__init__方法。换句话说,如果它是super(SomeBaseClass,self) super返回的代理对象将是object

对于多继承,MRO可以包含许多类,所以基本上super可以让您决定从MRO中的哪个位置开始搜索。

其他回答

所有这些不都假定基类是一个新型的类吗?

class A:
    def __init__(self):
        print("A.__init__()")

class B(A):
    def __init__(self):
        print("B.__init__()")
        super(B, self).__init__()

将不能在python2中工作。类A必须是new-style,即:类A(对象)

当调用super()解析到父类方法、实例方法或static方法的父版本时,我们希望将我们所在作用域的当前类作为第一个参数传递,以指示我们试图解析到哪个父作用域,并将感兴趣的对象作为第二个参数传递,以指示我们试图将该作用域应用到哪个对象。

考虑一个类层次结构a、B和C,其中每个类都是后面一个类的父类,并且a、B和C分别是每个类的实例。

super(B, b) 
# resolves to the scope of B's parent i.e. A 
# and applies that scope to b, as if b was an instance of A

super(C, c) 
# resolves to the scope of C's parent i.e. B
# and applies that scope to c

super(B, c) 
# resolves to the scope of B's parent i.e. A 
# and applies that scope to c

使用super和static方法

例如,在__new__()方法中使用super()

class A(object):
    def __new__(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        return super(A, cls).__new__(cls, *a, **kw)

解释:

1-尽管__new__()通常将调用类的引用作为其第一个参数,但在Python中它不是作为类方法实现的,而是作为静态方法实现的。也就是说,在直接调用__new__()时,对类的引用必须作为第一个参数显式传递:

# if you defined this
class A(object):
    def __new__(cls):
        pass

# calling this would raise a TypeError due to the missing argument
A.__new__()

# whereas this would be fine
A.__new__(A)

2-当调用super()来获取父类时,我们传递子类A作为它的第一个参数,然后传递对感兴趣对象的引用,在这种情况下,它是在调用A.__new__(cls)时传递的类引用。在大多数情况下,它也恰好是对子类的引用。在某些情况下,它可能不是,例如在多代继承的情况下。

super(A, cls)

3-因为作为一般规则__new__()是一个静态方法,super(a, cls)。__new__也将返回一个staticmethod,并且需要显式地提供所有参数,包括对感兴趣对象的引用,在本例中是cls。

super(A, cls).__new__(cls, *a, **kw)

4-不用super做同样的事情

class A(object):
    def __new__(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        return object.__new__(cls, *a, **kw)

在实例方法中使用super

例如,在__init__()中使用super()

class A(object): 
    def __init__(self, *a, **kw):
        # ...
        # you make some changes here
        # ...

        super(A, self).__init__(*a, **kw)

解释:

1- __init__是一个实例方法,这意味着它将实例引用作为第一个参数。当直接从实例调用时,引用会隐式传递,也就是说你不需要指定它:

# you try calling `__init__()` from the class without specifying an instance
# and a TypeError is raised due to the expected but missing reference
A.__init__() # TypeError ...

# you create an instance
a = A()

# you call `__init__()` from that instance and it works
a.__init__()

# you can also call `__init__()` with the class and explicitly pass the instance 
A.__init__(a)

2-在__init__()中调用super()时,我们将子类作为第一个参数传递,感兴趣的对象作为第二个参数传递,后者通常是对子类实例的引用。

super(A, self)

3-调用super(A, self)返回一个代理,它将解析作用域并将其应用到self,就像它现在是父类的实例一样。因为__init__()是一个实例方法,所以s.__init__(…)调用将隐式地将self引用作为第一个参数传递给父类的__init__()。

4-要在不使用super的情况下执行同样的操作,我们需要将对实例的引用显式传递给父版本的__init__()。

class A(object): 
    def __init__(self, *a, **kw):
        # ...
        # you make some changes here
        # ...

        object.__init__(self, *a, **kw)

在类方法中使用super

class A(object):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        print "A.alternate_constructor called"
        return cls(*a, **kw)

class B(A):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        print "B.alternate_constructor called"
        return super(B, cls).alternate_constructor(*a, **kw)

解释:

类方法可以直接从类中调用,并将类的引用作为其第一个参数。

# calling directly from the class is fine,
# a reference to the class is passed implicitly
a = A.alternate_constructor()
b = B.alternate_constructor()

2-当在类方法中调用super()以解析到父类的版本时,我们希望将当前子类作为第一个参数传递,以指示我们试图解析到哪个父类的作用域,并将感兴趣的对象作为第二个参数传递,以指示我们希望将该作用域应用到哪个对象,该对象通常是对子类本身或其子类之一的引用。

super(B, cls_or_subcls)

3-调用super(B, cls)解析到A的作用域并将其应用到cls。由于alternate_constructor()是一个类方法,调用super(B, cls).alternate_constructor(…)将隐式地将cls的引用作为第一个参数传递给a版本的alternate_constructor()

super(B, cls).alternate_constructor()

4-要在不使用super()的情况下实现同样的功能,您需要获得a .alternate_constructor()的未绑定版本的引用(即函数的显式版本)。简单地这样做是行不通的:

class B(A):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        print "B.alternate_constructor called"
        return A.alternate_constructor(cls, *a, **kw)

上面的方法不起作用,因为A.alternate_constructor()方法将对A的隐式引用作为其第一个参数。这里传递的cls是它的第二个参数。

class B(A):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        print "B.alternate_constructor called"
        # first we get a reference to the unbound 
        # `A.alternate_constructor` function 
        unbound_func = A.alternate_constructor.im_func
        # now we call it and pass our own `cls` as its first argument
        return unbound_func(cls, *a, **kw)

这里有一些很好的答案,但它们没有解决在层次结构中的不同类具有不同签名的情况下如何使用super()……特别是在__init__的情况下

为了回答这个问题并能够有效地使用super(),我建议阅读我的答案super()并更改合作方法的签名。

下面是这种情况的解决方案:

层次结构中的顶级类必须继承自自定义类,如SuperObject: 如果类可以接受不同的参数,始终将接收到的所有参数作为关键字参数传递给超函数,并且始终接受**kwargs。

class SuperObject:        
    def __init__(self, **kwargs):
        print('SuperObject')
        mro = type(self).__mro__
        assert mro[-1] is object
        if mro[-2] is not SuperObject:
            raise TypeError(
                'all top-level classes in this hierarchy must inherit from SuperObject',
                'the last class in the MRO should be SuperObject',
                f'mro={[cls.__name__ for cls in mro]}'
            )

        # super().__init__ is guaranteed to be object.__init__        
        init = super().__init__
        init()

使用示例:

class A(SuperObject):
    def __init__(self, **kwargs):
        print("A")
        super(A, self).__init__(**kwargs)

class B(SuperObject):
    def __init__(self, **kwargs):
        print("B")
        super(B, self).__init__(**kwargs)

class C(A):
    def __init__(self, age, **kwargs):
        print("C",f"age={age}")
        super(C, self).__init__(age=age, **kwargs)

class D(B):
    def __init__(self, name, **kwargs):
        print("D", f"name={name}")
        super(D, self).__init__(name=name, **kwargs)

class E(C,D):
    def __init__(self, name, age, *args, **kwargs):
        print( "E", f"name={name}", f"age={age}")
        super(E, self).__init__(name=name, age=age, *args, **kwargs)

E(name='python', age=28)

输出:

E name=python age=28
C age=28
A
D name=python
B
SuperObject

在多重继承的情况下,通常需要调用双亲的初始化式,而不仅仅是第一个。super()并不总是使用基类,而是在方法解析顺序(MRO)中查找下一个类,并将当前对象作为该类的实例返回。例如:

class Base(object):
    def __init__(self):
        print("initializing Base")

class ChildA(Base):
    def __init__(self):
        print("initializing ChildA")
        Base.__init__(self)

class ChildB(Base):
    def __init__(self):
        print("initializing ChildB")
        super().__init__()

class Grandchild(ChildA, ChildB):
    def __init__(self):
        print("initializing Grandchild")
        super().__init__()
        
Grandchild()

结果

initializing Grandchild
initializing ChildA
initializing Base

用super().__init__()替换Base.__init__(self)会导致

initializing Grandchild
initializing ChildA
initializing ChildB
initializing Base

根据需要。

class Child(SomeBaseClass):
    def __init__(self):
        SomeBaseClass.__init__(self)

这很容易理解。

class Child(SomeBaseClass):
    def __init__(self):
        super(Child, self).__init__()

如果你使用super(Child,self)会发生什么?

当创建子实例时,它的MRO(方法解析顺序)是基于继承的(Child, SomeBaseClass, object)顺序。(假设SomeBaseClass除了默认对象之外没有其他父对象)

通过在self实例的MRO中传递Child、self、super搜索,并返回Child的next代理对象,在本例中是SomeBaseClass,该对象随后调用SomeBaseClass的__init__方法。换句话说,如果它是super(SomeBaseClass,self) super返回的代理对象将是object

对于多继承,MRO可以包含许多类,所以基本上super可以让您决定从MRO中的哪个位置开始搜索。