有什么区别:

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

and:

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

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


当前回答

当调用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()解析到父类方法、实例方法或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()

每个Python实例都有一个创建它的类。 Python中的每个类都有一个祖先类链。 使用super()委托的方法工作到实例类的链中的下一个祖先。

例子

这个小例子涵盖了所有有趣的情况:

class A:
    def m(self):
        print('A')

class B(A):
    def m(self):
        print('B start')
        super().m()
        print('B end')
        
class C(A):
    def m(self):
        print('C start')
        super().m()
        print('C end')

class D(B, C):
    def m(self):
        print('D start')
        super().m()
        print('D end')

调用的确切顺序由调用该方法的实例决定:

>>> a = A()
>>> b = B()
>>> c = C()
>>> d = D()

例如a,没有super调用:

>>> a.m()
A

例如实例b,祖先链为b -> A ->对象:

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

>>> b.m()
B start
A
B end

例如c,父链为c -> A ->对象:

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

>>> c.m()
C start
A
C end

例如,祖先链d更有趣d -> B -> C -> A ->对象(mro代表方法解析顺序):

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

>>> d.m()
D start
B start
C start
A
C end
B end
D end

更多的信息

回答了“super在Python中做什么?”的问题后,下一个问题是如何有效地使用它。请看这个循序渐进的教程或者这个45分钟的视频。

考虑下面的代码:

class X():
    def __init__(self):
        print("X")

class Y(X):
    def __init__(self):
        # X.__init__(self)
        super(Y, self).__init__()
        print("Y")

class P(X):
    def __init__(self):
        super(P, self).__init__()
        print("P")

class Q(Y, P):
    def __init__(self):
        super(Q, self).__init__()
        print("Q")

Q()

如果将Y的构造函数更改为X.__init__,你将得到:

X
Y
Q

但是使用super(Y, self).__init__(),你会得到:

X
P
Y
Q

P或Q甚至可能涉及到另一个文件,当你写X和Y时,你不知道,所以基本上,当你写类Y(X)时,你不知道super(Child, self)会引用什么,甚至Y的签名也像Y(X)一样简单。这就是为什么超级可能是更好的选择。

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

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()并不总是使用基类,而是在方法解析顺序(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

根据需要。