问题:“ super”在Python中做什么?

之间有什么区别:

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

和:

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

我看到super在只有单一继承的类中经常使用它。我知道为什么您会在多重继承中使用它,但不清楚在这种情况下使用它的好处。

What’s the difference between:

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

and:

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

I’ve seen super being used quite a lot in classes with only single inheritance. I can see why you’d use it in multiple inheritance but am unclear as to what the advantages are of using it in this kind of situation.


回答 0

super()单一继承的好处很小-大多数情况下,您不必将基类的名称硬编码到使用其父方法的每个方法中。

但是,如果不使用,几乎不可能使用多重继承super()。这包括常见的惯用语,例如mixin,接口,抽象类等。这扩展到了以后扩展您的代码的代码。如果以后有人要编写扩展的类Child和mixin,则他们的代码将无法正常工作。

The benefits of super() in single-inheritance are minimal — mostly, you don’t have to hard-code the name of the base class into every method that uses its parent methods.

However, it’s almost impossible to use multiple-inheritance without super(). This includes common idioms like mixins, interfaces, abstract classes, etc. This extends to code that later extends yours. If somebody later wanted to write a class that extended Child and a mixin, their code would not work properly.


回答 1

有什么不同?

SomeBaseClass.__init__(self) 

表示呼叫SomeBaseClass的方式__init__。而

super(Child, self).__init__()

表示__init__Child实例的方法解析顺序(MRO)中遵循的父类调用绑定。

如果实例是Child的子类,则MRO中可能紧随其后的是另一个父级。

简单解释

当编写一个类时,您希望其他类能够使用它。super()使其他类更容易使用您正在编写的类。

正如鲍勃·马丁(Bob Martin)所说,好的架构可以使您尽可能长地推迟决策。

super() 可以实现这种架构。

当另一个类对您编写的类进行子类化时,它也可能继承自其他类。而这些类可以有一个__init__在此之后来自__init__基于类的进行方法解析顺序。

如果没有super您,可能会硬编码您正在编写的类的父级(如示例中所示)。这意味着您将不会__init__在MRO中调用下一个,因此您将无法重用其中的代码。

如果您正在编写自己的代码供个人使用,则可能不必担心这种区别。但是,如果您希望其他人使用您的代码,则使用super一件事可以为代码用户提供更大的灵活性。

Python 2与3

这适用于Python 2和3:

super(Child, self).__init__()

这仅适用于Python 3:

super().__init__()

它不带任何参数,方法是在堆栈框架中上移并获取方法的第一个参数(通常self用于实例方法或cls类方法-但可以是其他名称),然后Child在自由变量中找到类(例如)(__class__在方法中将其作为自由闭合变量的名称进行查找)。

我更喜欢演示使用的交叉兼容方式super,但是如果您仅使用Python 3,则可以不带任何参数调用它。

具有前向兼容性的间接

它给你什么?对于单继承,从静态分析的角度来看,问题的示例实际上是相同的。但是,使用super会为您提供具有向前兼容性的间接层。

前向兼容性对经验丰富的开发人员非常重要。您希望代码在更改时保持最少的更改。当您查看修订历史记录时,您希望确切地看到更改的时间。

您可以从单一继承开始,但是如果您决定添加另一个基类,则只需要更改基数行即可-如果基类在您继承的类中发生了变化(例如添加了mixin),则可以进行更改这个班没什么。特别是在Python 2中,super很难正确获取参数和正确的方法参数。如果您知道super正确地使用了单继承,那么调试就不会那么困难了。

依赖注入

其他人可以使用您的代码并将父级注入方法解析中:

class SomeBaseClass(object):
    def __init__(self):
        print('SomeBaseClass.__init__(self) called')

class UnsuperChild(SomeBaseClass):
    def __init__(self):
        print('UnsuperChild.__init__(self) called')
        SomeBaseClass.__init__(self)

class SuperChild(SomeBaseClass):
    def __init__(self):
        print('SuperChild.__init__(self) called')
        super(SuperChild, self).__init__()

假设您向对象添加了另一个类,并想在Foo和Bar之间注入一个类(出于测试或其他原因):

class InjectMe(SomeBaseClass):
    def __init__(self):
        print('InjectMe.__init__(self) called')
        super(InjectMe, self).__init__()

class UnsuperInjector(UnsuperChild, InjectMe): pass

class SuperInjector(SuperChild, InjectMe): pass

使用un-super子级无法注入依赖项,因为您正在使用的子级已经硬编码了要在其自身之后调用的方法:

>>> o = UnsuperInjector()
UnsuperChild.__init__(self) called
SomeBaseClass.__init__(self) called

但是,带有子级的类super可以正确注入依赖项:

>>> o2 = SuperInjector()
SuperChild.__init__(self) called
InjectMe.__init__(self) called
SomeBaseClass.__init__(self) called

发表评论

为什么这在世界上有用?

Python通过C3线性化算法将复杂的继承树线性化,以创建方法解析顺序(MRO)。

我们希望按该顺序查找方法。

对于在父级中定义的方法,如果不按顺序查找下一个方法super,则必须

  1. 从实例的类型获取mro
  2. 寻找定义方法的类型
  3. 用该方法找到下一个类型
  4. 绑定该方法并使用所需的参数调用它

UnsuperChild不该访问InjectMe。为什么没有“总是避免使用super” 的结论?我在这里想念什么?

UnsuperChild不会访问InjectMe。可以UnsuperInjector访问InjectMe-却无法从其继承的方法调用该类的方法UnsuperChild

两个Child类都打算使用MRO中紧随其后的相同名称来调用方法,这可能是它在创建时不知道的另一个类。

没有super硬编码其父方法的方法-因此限制了其方法的行为,并且子类无法在调用链中注入功能。

在一个 super具有更大的灵活性。这些方法的调用链可以被拦截并注入功能。

您可能不需要该功能,但是代码的子类却可能需要。

结论

始终使用super引用父类而不是对其进行硬编码。

您打算引用的是下一行的父类,而不是您看到子级继承的父类。

不使用super会给您的代码用户带来不必要的限制。

What’s the difference?

SomeBaseClass.__init__(self) 

means to call SomeBaseClass‘s __init__. while

super(Child, self).__init__()

means to call a bound __init__ from the parent class that follows Child in the instance’s Method Resolution Order (MRO).

If the instance is a subclass of Child, there may be a different parent that comes next in the MRO.

Explained simply

When you write a class, you want other classes to be able to use it. super() makes it easier for other classes to use the class you’re writing.

As Bob Martin says, a good architecture allows you to postpone decision making as long as possible.

super() can enable that sort of architecture.

When another class subclasses the class you wrote, it could also be inheriting from other classes. And those classes could have an __init__ that comes after this __init__ based on the ordering of the classes for method resolution.

Without super you would likely hard-code the parent of the class you’re writing (like the example does). This would mean that you would not call the next __init__ in the MRO, and you would thus not get to reuse the code in it.

If you’re writing your own code for personal use, you may not care about this distinction. But if you want others to use your code, using super is one thing that allows greater flexibility for users of the code.

Python 2 versus 3

This works in Python 2 and 3:

super(Child, self).__init__()

This only works in Python 3:

super().__init__()

It works with no arguments by moving up in the stack frame and getting the first argument to the method (usually self for an instance method or cls for a class method – but could be other names) and finding the class (e.g. Child) in the free variables (it is looked up with the name __class__ as a free closure variable in the method).

I prefer to demonstrate the cross-compatible way of using super, but if you are only using Python 3, you can call it with no arguments.

Indirection with Forward Compatibility

What does it give you? For single inheritance, the examples from the question are practically identical from a static analysis point of view. However, using super gives you a layer of indirection with forward compatibility.

Forward compatibility is very important to seasoned developers. You want your code to keep working with minimal changes as you change it. When you look at your revision history, you want to see precisely what changed when.

You may start off with single inheritance, but if you decide to add another base class, you only have to change the line with the bases – if the bases change in a class you inherit from (say a mixin is added) you’d change nothing in this class. Particularly in Python 2, getting the arguments to super and the correct method arguments right can be difficult. If you know you’re using super correctly with single inheritance, that makes debugging less difficult going forward.

Dependency Injection

Other people can use your code and inject parents into the method resolution:

class SomeBaseClass(object):
    def __init__(self):
        print('SomeBaseClass.__init__(self) called')

class UnsuperChild(SomeBaseClass):
    def __init__(self):
        print('UnsuperChild.__init__(self) called')
        SomeBaseClass.__init__(self)

class SuperChild(SomeBaseClass):
    def __init__(self):
        print('SuperChild.__init__(self) called')
        super(SuperChild, self).__init__()

Say you add another class to your object, and want to inject a class between Foo and Bar (for testing or some other reason):

class InjectMe(SomeBaseClass):
    def __init__(self):
        print('InjectMe.__init__(self) called')
        super(InjectMe, self).__init__()

class UnsuperInjector(UnsuperChild, InjectMe): pass

class SuperInjector(SuperChild, InjectMe): pass

Using the un-super child fails to inject the dependency because the child you’re using has hard-coded the method to be called after its own:

>>> o = UnsuperInjector()
UnsuperChild.__init__(self) called
SomeBaseClass.__init__(self) called

However, the class with the child that uses super can correctly inject the dependency:

>>> o2 = SuperInjector()
SuperChild.__init__(self) called
InjectMe.__init__(self) called
SomeBaseClass.__init__(self) called

Addressing a comment

Why in the world would this be useful?

Python linearizes a complicated inheritance tree via the C3 linearization algorithm to create a Method Resolution Order (MRO).

We want methods to be looked up in that order.

For a method defined in a parent to find the next one in that order without super, it would have to

  1. get the mro from the instance’s type
  2. look for the type that defines the method
  3. find the next type with the method
  4. bind that method and call it with the expected arguments

The UnsuperChild should not have access to InjectMe. Why isn’t the conclusion “Always avoid using super“? What am I missing here?

The UnsuperChild does not have access to InjectMe. It is the UnsuperInjector that has access to InjectMe – and yet cannot call that class’s method from the method it inherits from UnsuperChild.

Both Child classes intend to call a method by the same name that comes next in the MRO, which might be another class it was not aware of when it was created.

The one without super hard-codes its parent’s method – thus is has restricted the behavior of its method, and subclasses cannot inject functionality in the call chain.

The one with super has greater flexibility. The call chain for the methods can be intercepted and functionality injected.

You may not need that functionality, but subclassers of your code may.

Conclusion

Always use super to reference the parent class instead of hard-coding it.

What you intend is to reference the parent class that is next-in-line, not specifically the one you see the child inheriting from.

Not using super can put unnecessary constraints on users of your code.


回答 2

我与玩了一点super(),并意识到我们可以更改通话顺序。

例如,我们有下一个层次结构:

    A
   / \
  B   C
   \ /
    D

在这种情况下,D的MRO将是(仅适用于Python 3):

In [26]: D.__mro__
Out[26]: (__main__.D, __main__.B, __main__.C, __main__.A, object)

让我们创建一个super()方法执行后调用的类。

In [23]: class A(object): #  or with Python 3 can define class A:
...:     def __init__(self):
...:         print("I'm from A")
...:  
...: class B(A):
...:      def __init__(self):
...:          print("I'm from B")
...:          super().__init__()
...:   
...: class C(A):
...:      def __init__(self):
...:          print("I'm from C")
...:          super().__init__()
...:  
...: class D(B, C):
...:      def __init__(self):
...:          print("I'm from D")
...:          super().__init__()
...: d = D()
...:
I'm from D
I'm from B
I'm from C
I'm from A

    A
   / 
  B  C
    /
    D

因此,我们可以看到解析顺序与MRO中的解析顺序相同。但是当我们super()在方法的开头调用时:

In [21]: class A(object):  # or class A:
...:     def __init__(self):
...:         print("I'm from A")
...:  
...: class B(A):
...:      def __init__(self):
...:          super().__init__()  # or super(B, self).__init_()
...:          print("I'm from B")
...:   
...: class C(A):
...:      def __init__(self):
...:          super().__init__()
...:          print("I'm from C")
...:  
...: class D(B, C):
...:      def __init__(self):
...:          super().__init__()
...:          print("I'm from D")
...: d = D()
...: 
I'm from A
I'm from C
I'm from B
I'm from D

我们有一个不同的顺序,它是MRO元组的相反顺序。

    A
   / 
  B  C
    /
    D 

如需其他阅读,我建议下一个答案:

  1. 具有超级(大型层次结构)的C3线性化示例
  2. 新旧样式类之间的重要行为更改
  3. 新型课堂的内幕故事

I had played a bit with super(), and had recognized that we can change calling order.

For example, we have next hierarchy structure:

    A
   / \
  B   C
   \ /
    D

In this case MRO of D will be (only for Python 3):

In [26]: D.__mro__
Out[26]: (__main__.D, __main__.B, __main__.C, __main__.A, object)

Let’s create a class where super() calls after method execution.

In [23]: class A(object): #  or with Python 3 can define class A:
...:     def __init__(self):
...:         print("I'm from A")
...:  
...: class B(A):
...:      def __init__(self):
...:          print("I'm from B")
...:          super().__init__()
...:   
...: class C(A):
...:      def __init__(self):
...:          print("I'm from C")
...:          super().__init__()
...:  
...: class D(B, C):
...:      def __init__(self):
...:          print("I'm from D")
...:          super().__init__()
...: d = D()
...:
I'm from D
I'm from B
I'm from C
I'm from A

    A
   / ⇖
  B ⇒ C
   ⇖ /
    D

So we can see that resolution order is same as in MRO. But when we call super() in the beginning of the method:

In [21]: class A(object):  # or class A:
...:     def __init__(self):
...:         print("I'm from A")
...:  
...: class B(A):
...:      def __init__(self):
...:          super().__init__()  # or super(B, self).__init_()
...:          print("I'm from B")
...:   
...: class C(A):
...:      def __init__(self):
...:          super().__init__()
...:          print("I'm from C")
...:  
...: class D(B, C):
...:      def __init__(self):
...:          super().__init__()
...:          print("I'm from D")
...: d = D()
...: 
I'm from A
I'm from C
I'm from B
I'm from D

We have a different order it is reversed a order of the MRO tuple.

    A
   / ⇘
  B ⇐ C
   ⇘ /
    D 

For additional reading I would recommend next answers:

  1. C3 linearization example with super (a large hierarchy)
  2. Important behavior changes between old and new style classes
  3. The Inside Story on New-Style Classes

回答 3

难道不是所有这些都假设基类是新型类吗?

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

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

在Python 2中将无法使用。class A必须是新样式,即:class A(object)

Doesn’t all of this assume that the base class is a new-style class?

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

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

Will not work in Python 2. class A must be new-style, i.e: class A(object)


回答 4

当调用super()解析为父方法的类方法,实例方法或静态方法时,我们希望将其所在范围的当前类作为第一个参数传递,以指示我们要解析为哪个父方法的范围,并作为第二个参数是感兴趣的对象,用于指示我们要将该范围应用于哪个对象。

考虑一个类层次结构AB以及C其中,每个类是一个跟随它的父,并且abc每个的相应实例。

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与静态方法一起使用

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

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__因此也将返回静态方法,并且需要显式提供所有参数,在这种情况下,包括对insterest对象的引用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与实例方法

例如super()从内部使用__init__()

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-当super()在内部调用时,__init__()我们将子类作为第一个参数,将感兴趣的对象作为第二个参数,这通常是对子类实例的引用。

super(A, self)

3-调用super(A, self)返回一个代理,它将解析作用域并将其应用于self当前的父类实例。让我们称该代理s。由于__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)

说明:

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

# 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)

When calling super() to resolve to a parent’s version of a classmethod, instance method, or staticmethod, we want to pass the current class whose scope we are in as the first argument, to indicate which parent’s scope we’re trying to resolve to, and as a second argument the object of interest to indicate which object we’re trying to apply that scope to.

Consider a class hierarchy A, B, and C where each class is the parent of the one following it, and a, b, and c respective instances of each.

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

Using super with a staticmethod

e.g. using super() from within the __new__() method

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

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

Explanation:

1- even though it’s usual for __new__() to take as its first param a reference to the calling class, it is not implemented in Python as a classmethod, but rather a staticmethod. That is, a reference to a class has to be passed explicitly as the first argument when calling __new__() directly:

# 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- when calling super() to get to the parent class we pass the child class A as its first argument, then we pass a reference to the object of interest, in this case it’s the class reference that was passed when A.__new__(cls) was called. In most cases it also happens to be a reference to the child class. In some situations it might not be, for instance in the case of multiple generation inheritances.

super(A, cls)

3- since as a general rule __new__() is a staticmethod, super(A, cls).__new__ will also return a staticmethod and needs to be supplied all arguments explicitly, including the reference to the object of insterest, in this case cls.

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

4- doing the same thing without super

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

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

Using super with an instance method

e.g. using super() from within __init__()

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

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

Explanation:

1- __init__ is an instance method, meaning that it takes as its first argument a reference to an instance. When called directly from the instance, the reference is passed implicitly, that is you don’t need to specify it:

# 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- when calling super() within __init__() we pass the child class as the first argument and the object of interest as a second argument, which in general is a reference to an instance of the child class.

super(A, self)

3- The call super(A, self) returns a proxy that will resolve the scope and apply it to self as if it’s now an instance of the parent class. Let’s call that proxy s. Since __init__() is an instance method the call s.__init__(...) will implicitly pass a reference of self as the first argument to the parent’s __init__().

4- to do the same without super we need to pass a reference to an instance explicitly to the parent’s version of __init__().

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

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

Using super with a classmethod

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)

Explanation:

1- A classmethod can be called from the class directly and takes as its first parameter a reference to the class.

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

2- when calling super() within a classmethod to resolve to its parent’s version of it, we want to pass the current child class as the first argument to indicate which parent’s scope we’re trying to resolve to, and the object of interest as the second argument to indicate which object we want to apply that scope to, which in general is a reference to the child class itself or one of its subclasses.

super(B, cls_or_subcls)

3- The call super(B, cls) resolves to the scope of A and applies it to cls. Since alternate_constructor() is a classmethod the call super(B, cls).alternate_constructor(...) will implicitly pass a reference of cls as the first argument to A‘s version of alternate_constructor()

super(B, cls).alternate_constructor()

4- to do the same without using super() you would need to get a reference to the unbound version of A.alternate_constructor() (i.e. the explicit version of the function). Simply doing this would not work:

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)

The above would not work because the A.alternate_constructor() method takes an implicit reference to A as its first argument. The cls being passed here would be its second argument.

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)

回答 5

有很多不错的答案,但是对于视觉学习者来说:首先让我们以super为参数进行探索,然后再以super为参数。 超级继承树示例

想象有一个jack从类创建的实例,该实例Jack具有继承链,如图中的绿色所示。调用:

super(Jack, jack).method(...)

将使用(jack按一定顺序的继承树)的MRO(方法解析顺序),并从开始搜索Jack。为什么可以提供家长班?好吧,如果我们从实例开始搜索jack,它将找到实例方法,重点是找到其父方法。

如果不向super提供参数,则其像传入的第一个参数是的类self,而传入的第二个参数是self。这些是在Python3中自动为您计算的。

但是请说我们不想使用Jack的方法,而不是传入Jack,我们可以传入从Jen开始向上搜索该方法Jen

它一次搜索一层(宽度而不是深度),例如,如果AdamSue两者都具有所需的方法,Sue将首先找到其中的一层。

如果CainSue都具有必需的方法,Cain则将首先调用的方法。这在代码中对应于:

Class Jen(Cain, Sue):

MRO是从左到右。

Many great answers, but for visual learners: Firstly lets explore with arguments to super, and then without. super inheritance tree example

Imagine theres an instance jack created from the class Jack, who has the inheritance chain as shown in green in the picture. Calling:

super(Jack, jack).method(...)

will use the MRO (Method Resolution Order) of jack (its inheritance tree in a certain order), and will start searching from Jack. Why can one provide a parent class? Well if we start searching from the instance jack, it would find the instance method, the whole point is to find its parents method.

If one does not supply arguments to super, its like the first argument passed in is the class of self, and the second argument passed in is self. These are auto-calculated for you in Python3.

However say we dont want to use Jack‘s method, instead of passing in Jack, we could of passed in Jen to start searching upwards for the method from Jen.

It searches one layer at a time (width not depth), e.g. if Adam and Sue both have the required method, the one from Sue will be found first.

If Cain and Sue both had the required method, Cain‘s method would be called first. This corresponds in code to:

Class Jen(Cain, Sue):

MRO is from left to right.


回答 6

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

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

这只是这种情况的解决方案:

  1. 层次结构中的顶级类必须继承自定义类,例如SuperObject
  2. 如果类可以采用不同的参数,则始终将您收到的所有参数作为关键字参数传递给超函数,并始终接受**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

some great answers here, but they do not tackle how to use super() in the case where different classes in the hierarchy have different signatures … especially in the case of __init__

to answer that part and to be able to effectively use super() i’d suggest reading my answer super() and changing the signature of cooperative methods.

here’s just the solution to this scenario:

  1. the top-level classes in your hierarchy must inherit from a custom class like SuperObject:
  2. if classes can take differing arguments, always pass all arguments you received on to the super function as keyword arguments, and, always accept **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()

example usage:

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)

output:

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

回答 7

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

这很容易理解。

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

好的,如果您现在使用该super(Child,self)怎么办?

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

通过传递Child, selfsuperself实例的MRO中搜索,然后返回Child的下一个代理对象(在本例中为SomeBaseClass),然后此对象调用__init__SomeBaseClass 的方法。换句话说,如果是super(SomeBaseClass,self),则super返回的代理对象将是object

对于多继承,MRO可以包含许多类,因此基本上super可以让您决定要在MRO中开始搜索的位置。

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

This is fairly easy to understand.

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

Ok, what happens now if you use super(Child,self)?

When a Child instance is created, its MRO(Method Resolution Order) is in the order of (Child, SomeBaseClass, object) based on the inheritance. (assume SomeBaseClass doesn’t have other parents except for the default object)

By passing Child, self, super searches in the MRO of the self instance, and return the proxy object next of Child, in this case it’s SomeBaseClass, this object then invokes the __init__ method of SomeBaseClass. In other word, if it’s super(SomeBaseClass,self), the proxy object that super returns would be object

For multi inheritance, the MRO could contain many classes, so basically super lets you decide where you want to start searching in the MRO.


回答 8

考虑以下代码:

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()

如果将的构造函数更改YX.__init__,您将获得:

X
Y
Q

但是使用super(Y, self).__init__(),您将获得:

X
P
Y
Q

P或者Q甚至可以从当你写你不知道另一个文件参与XY。因此,基本上,即使Y的签名与一样简单,您也不知道super(Child, self)在编写时将引用什么内容。这就是为什么超级可能是更好的选择。class Y(X)Y(X)

Consider the following code:

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()

If change constructor of Y to X.__init__, you will get:

X
Y
Q

But using super(Y, self).__init__(), you will get:

X
P
Y
Q

And P or Q may even be involved from another file which you don’t know when you writing X and Y. So, basically, you won’t know what super(Child, self) will reference to when you are writing class Y(X), even the signature of Y is as simple as Y(X). That’s why super could be a better choice.


声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。