标签归档:super

用多重继承调用父类__init__,正确的方法是什么?

问题:用多重继承调用父类__init__,正确的方法是什么?

假设我有多个继承方案:

class A(object):
    # code for A here

class B(object):
    # code for B here

class C(A, B):
    def __init__(self):
        # What's the right code to write here to ensure 
        # A.__init__ and B.__init__ get called?

有编写的两个典型方法C__init__

  1. (老式) ParentClass.__init__(self)
  2. (较新的样式) super(DerivedClass, self).__init__()

但是,在任何一种情况下,如果父类(AB没有遵循相同的约定,则代码将无法正常工作(某些代码可能会丢失或多次调用)。

那么又是什么正确的方法呢?说“保持一致,遵循一个或另一个”很容易,但是如果AB来自第三方图书馆,那又如何呢?有没有一种方法可以确保所有父类构造函数都被调用(以正确的顺序,并且只能调用一次)?

编辑:看看我的意思,如果我这样做:

class A(object):
    def __init__(self):
        print("Entering A")
        super(A, self).__init__()
        print("Leaving A")

class B(object):
    def __init__(self):
        print("Entering B")
        super(B, self).__init__()
        print("Leaving B")

class C(A, B):
    def __init__(self):
        print("Entering C")
        A.__init__(self)
        B.__init__(self)
        print("Leaving C")

然后我得到:

Entering C
Entering A
Entering B
Leaving B
Leaving A
Entering B
Leaving B
Leaving C

请注意,Binit会被调用两次。如果我做:

class A(object):
    def __init__(self):
        print("Entering A")
        print("Leaving A")

class B(object):
    def __init__(self):
        print("Entering B")
        super(B, self).__init__()
        print("Leaving B")

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

然后我得到:

Entering C
Entering A
Leaving A
Leaving C

请注意,B永远不会调用init。因此,似乎除非我知道/控制我从(AB)继承的类的初始化,否则我无法对正在编写的类(C)做出安全选择。

Say I have a multiple inheritance scenario:

class A(object):
    # code for A here

class B(object):
    # code for B here

class C(A, B):
    def __init__(self):
        # What's the right code to write here to ensure 
        # A.__init__ and B.__init__ get called?

There’s two typical approaches to writing C‘s __init__:

  1. (old-style) ParentClass.__init__(self)
  2. (newer-style) super(DerivedClass, self).__init__()

However, in either case, if the parent classes (A and B) don’t follow the same convention, then the code will not work correctly (some may be missed, or get called multiple times).

So what’s the correct way again? It’s easy to say “just be consistent, follow one or the other”, but if A or B are from a 3rd party library, what then? Is there an approach that can ensure that all parent class constructors get called (and in the correct order, and only once)?

Edit: to see what I mean, if I do:

class A(object):
    def __init__(self):
        print("Entering A")
        super(A, self).__init__()
        print("Leaving A")

class B(object):
    def __init__(self):
        print("Entering B")
        super(B, self).__init__()
        print("Leaving B")

class C(A, B):
    def __init__(self):
        print("Entering C")
        A.__init__(self)
        B.__init__(self)
        print("Leaving C")

Then I get:

Entering C
Entering A
Entering B
Leaving B
Leaving A
Entering B
Leaving B
Leaving C

Note that B‘s init gets called twice. If I do:

class A(object):
    def __init__(self):
        print("Entering A")
        print("Leaving A")

class B(object):
    def __init__(self):
        print("Entering B")
        super(B, self).__init__()
        print("Leaving B")

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

Then I get:

Entering C
Entering A
Leaving A
Leaving C

Note that B‘s init never gets called. So it seems that unless I know/control the init’s of the classes I inherit from (A and B) I cannot make a safe choice for the class I’m writing (C).


回答 0

两种方式都可以正常工作。使用该方法super()可为子类带来更大的灵活性。

在直接呼叫方式中,C.__init__可以同时呼叫A.__init__B.__init__

使用时super(),需要将类设计为在其中C调用的协作式多重继承super,这将调用A的代码,该代码还将super调用B的代码。请参阅http://rhettinger.wordpress.com/2011/05/26/super-considered-super,以详细了解可以使用进行的操作super

[回答问题,稍后编辑]

因此,似乎除非我知道/控制我从(A和B)继承的类的初始化,否则我无法对我正在编写的类(C)做出安全的选择。

参考的文章显示了如何通过在A和周围添加包装器类来处理这种情况B。标题为“如何合并非合作类”的部分提供了一个可行的示例。

可能希望多重继承更容易,让您轻松组成Car和Airplane类来获得FlyingCar,但现实情况是,单独设计的组件通常需要适配器或包装器,然后才能像我们希望的那样无缝地组装在一起:-)

另一个想法:如果您对使用多重继承来编写功能不满意,则可以使用composition来完全控制在哪些情况下调用哪种方法。

Both ways work fine. The approach using super() leads to greater flexibility for subclasses.

In the direct call approach, C.__init__ can call both A.__init__ and B.__init__.

When using super(), the classes need to be designed for cooperative multiple inheritance where C calls super, which invokes A‘s code which will also call super which invokes B‘s code. See http://rhettinger.wordpress.com/2011/05/26/super-considered-super for more detail on what can be done with super.

[Response question as later edited]

So it seems that unless I know/control the init’s of the classes I inherit from (A and B) I cannot make a safe choice for the class I’m writing (C).

The referenced article shows how to handle this situation by adding a wrapper class around A and B. There is a worked-out example in the section titled “How to Incorporate a Non-cooperative Class”.

One might wish that multiple inheritance were easier, letting you effortlessly compose Car and Airplane classes to get a FlyingCar, but the reality is that separately designed components often need adapters or wrappers before fitting together as seamlessly as we would like :-)

One other thought: if you’re unhappy with composing functionality using multiple inheritance, you can use composition for complete control over which methods get called on which occasions.


回答 1

您问题的答案取决于一个非常重要的方面:您的基类是否设计用于多重继承?

有3种不同的方案:

  1. 基类是不相关的独立类。

    如果您的基类是能够独立运行的独立实体,并且彼此之间不认识,则它们不是为多重继承设计的。例:

    class Foo:
        def __init__(self):
            self.foo = 'foo'
    
    class Bar:
        def __init__(self, bar):
            self.bar = bar
    

    重要:请注意,既不打电话Foo也不Bar打电话super().__init__()!这就是为什么您的代码无法正常工作的原因。由于Diamond继承在python中的工作方式,因此object不应调用基类为的类super().__init__()。如您所知,这样做会破坏多重继承,因为您最终将调用另一个类的__init__而不是object.__init__()免责声明:避免super().__init__()object-subclasses中是我个人的建议,绝不是python社区中达成一致的共识。有些人更喜欢super在每个类中使用,认为如果该类的行为不像您通常可以编写一个适配器您期望的。)

    这也意味着您永远不应编写从其继承object且没有__init__方法的类。完全不定义__init__方法与调用具有相同的效果super().__init__()。如果您的类直接继承自object,请确保添加一个空的构造函数,如下所示:

    class Base(object):
        def __init__(self):
            pass
    

    无论如何,在这种情况下,您将必须手动调用每个父构造函数。有两种方法可以做到这一点:

    • 不带 super

      class FooBar(Foo, Bar):
          def __init__(self, bar='bar'):
              Foo.__init__(self)  # explicit calls without super
              Bar.__init__(self, bar)
      
    • super

      class FooBar(Foo, Bar):
          def __init__(self, bar='bar'):
              super().__init__()  # this calls all constructors up to Foo
              super(Foo, self).__init__(bar)  # this calls all constructors after Foo up
                                              # to Bar
      

    这两种方法各有其优点和缺点。如果你使用super,你的类将支持依赖注入。另一方面,容易出错。例如,如果你改变的顺序FooBar(像class FooBar(Bar, Foo)),你就必须更新super到匹配的电话。没有super您,不必担心这一点,并且代码更具可读性。

  2. 类之一是mixin。

    混入是,这是一个一流的设计与多重继承使用。这意味着我们不必手动调用两个父构造函数,因为mixin会自动为我们调用第二个构造函数。由于这次只需要调用一个构造函数,因此super可以避免对父类的名称进行硬编码。

    例:

    class FooMixin:
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)  # forwards all unused arguments
            self.foo = 'foo'
    
    class Bar:
        def __init__(self, bar):
            self.bar = bar
    
    class FooBar(FooMixin, Bar):
        def __init__(self, bar='bar'):
            super().__init__(bar)  # a single call is enough to invoke
                                   # all parent constructors
    
            # NOTE: `FooMixin.__init__(self, bar)` would also work, but isn't
            # recommended because we don't want to hard-code the parent class.
    

    这里的重要细节是:

    • mixin调用super().__init__()并通过它接收的任何参数。
    • 子类首先从mixin继承:class FooBar(FooMixin, Bar)。如果基类的顺序错误,则将永远不会调用mixin的构造函数。
  3. 所有基类均设计用于协作继承。

    专为合作继承而设计的类非常类似于mixin:它们将所有未使用的参数传递给下一类。和以前一样,我们只需要调用即可super().__init__(),所有父级构造函数都将被链调用。

    例:

    class CoopFoo:
        def __init__(self, **kwargs):
            super().__init__(**kwargs)  # forwards all unused arguments
            self.foo = 'foo'
    
    class CoopBar:
        def __init__(self, bar, **kwargs):
            super().__init__(**kwargs)  # forwards all unused arguments
            self.bar = bar
    
    class CoopFooBar(CoopFoo, CoopBar):
        def __init__(self, bar='bar'):
            super().__init__(bar=bar)  # pass all arguments on as keyword
                                       # arguments to avoid problems with
                                       # positional arguments and the order
                                       # of the parent classes
    

    在这种情况下,父类的顺序无关紧要。我们CoopBar最好还是从头继承,而代码仍然可以正常工作。但这是真的,因为所有参数都作为关键字参数传递。使用位置参数将很容易弄错参数的顺序,因此,协作类习惯于仅接受关键字参数。

    这也是我前面提到的规则的一个exceptions:CoopFooCoopBar都继承自object,但它们仍然调用super().__init__()。如果没有,则不会有合作继承。

底线:正确的实现取决于您从其继承的类。

构造函数是类的公共接口的一部分。如果该类被设计为混合或协作继承,则必须将其记录下来。如果文档中未提及任何内容,则可以安全地假定该类不是为协作多重继承设计的。

The answer to your question depends on one very important aspect: Are your base classes designed for multiple inheritance?

There are 3 different scenarios:

  1. The base classes are unrelated, standalone classes.

    If your base classes are separate entities that are capable of functioning independently and they don’t know each other, they’re not designed for multiple inheritance. Example:

    class Foo:
        def __init__(self):
            self.foo = 'foo'
    
    class Bar:
        def __init__(self, bar):
            self.bar = bar
    

    Important: Notice that neither Foo nor Bar calls super().__init__()! This is why your code didn’t work correctly. Because of the way diamond inheritance works in python, classes whose base class is object should not call super().__init__(). As you’ve noticed, doing so would break multiple inheritance because you end up calling another class’s __init__ rather than object.__init__(). (Disclaimer: Avoiding super().__init__() in object-subclasses is my personal recommendation and by no means an agreed-upon consensus in the python community. Some people prefer to use super in every class, arguing that you can always write an adapter if the class doesn’t behave as you expect.)

    This also means that you should never write a class that inherits from object and doesn’t have an __init__ method. Not defining a __init__ method at all has the same effect as calling super().__init__(). If your class inherits directly from object, make sure to add an empty constructor like so:

    class Base(object):
        def __init__(self):
            pass
    

    Anyway, in this situation, you will have to call each parent constructor manually. There are two ways to do this:

    • Without super

      class FooBar(Foo, Bar):
          def __init__(self, bar='bar'):
              Foo.__init__(self)  # explicit calls without super
              Bar.__init__(self, bar)
      
    • With super

      class FooBar(Foo, Bar):
          def __init__(self, bar='bar'):
              super().__init__()  # this calls all constructors up to Foo
              super(Foo, self).__init__(bar)  # this calls all constructors after Foo up
                                              # to Bar
      

    Each of these two methods has its own advantages and disadvantages. If you use super, your class will support dependency injection. On the other hand, it’s easier to make mistakes. For example if you change the order of Foo and Bar (like class FooBar(Bar, Foo)), you’d have to update the super calls to match. Without super you don’t have to worry about this, and the code is much more readable.

  2. One of the classes is a mixin.

    A mixin is a class that’s designed to be used with multiple inheritance. This means we don’t have to call both parent constructors manually, because the mixin will automatically call the 2nd constructor for us. Since we only have to call a single constructor this time, we can do so with super to avoid having to hard-code the parent class’s name.

    Example:

    class FooMixin:
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)  # forwards all unused arguments
            self.foo = 'foo'
    
    class Bar:
        def __init__(self, bar):
            self.bar = bar
    
    class FooBar(FooMixin, Bar):
        def __init__(self, bar='bar'):
            super().__init__(bar)  # a single call is enough to invoke
                                   # all parent constructors
    
            # NOTE: `FooMixin.__init__(self, bar)` would also work, but isn't
            # recommended because we don't want to hard-code the parent class.
    

    The important details here are:

    • The mixin calls super().__init__() and passes through any arguments it receives.
    • The subclass inherits from the mixin first: class FooBar(FooMixin, Bar). If the order of the base classes is wrong, the mixin’s constructor will never be called.
  3. All base classes are designed for cooperative inheritance.

    Classes designed for cooperative inheritance are a lot like mixins: They pass through all unused arguments to the next class. Like before, we just have to call super().__init__() and all parent constructors will be chain-called.

    Example:

    class CoopFoo:
        def __init__(self, **kwargs):
            super().__init__(**kwargs)  # forwards all unused arguments
            self.foo = 'foo'
    
    class CoopBar:
        def __init__(self, bar, **kwargs):
            super().__init__(**kwargs)  # forwards all unused arguments
            self.bar = bar
    
    class CoopFooBar(CoopFoo, CoopBar):
        def __init__(self, bar='bar'):
            super().__init__(bar=bar)  # pass all arguments on as keyword
                                       # arguments to avoid problems with
                                       # positional arguments and the order
                                       # of the parent classes
    

    In this case, the order of the parent classes doesn’t matter. We might as well inherit from CoopBar first, and the code would still work the same. But that’s only true because all arguments are passed as keyword arguments. Using positional arguments would make it easy to get the order of the arguments wrong, so it’s customary for cooperative classes to accept only keyword arguments.

    This is also an exception to the rule I mentioned earlier: Both CoopFoo and CoopBar inherit from object, but they still call super().__init__(). If they didn’t, there would be no cooperative inheritance.

Bottom line: The correct implementation depends on the classes you’re inheriting from.

The constructor is part of a class’s public interface. If the class is designed as a mixin or for cooperative inheritance, that must be documented. If the docs don’t mention anything of the sort, it’s safe to assume that the class isn’t designed for cooperative multiple inheritance.


回答 2

这两种方法(“新风格”或“旧式”),将工作,如果你有过的源代码控制AB。否则,可能需要使用适配器类。

可访问的源代码:正确使用“新样式”

class A(object):
    def __init__(self):
        print("-> A")
        super(A, self).__init__()
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        super(B, self).__init__()
        print("<- B")

class C(A, B):
    def __init__(self):
        print("-> C")
        # Use super here, instead of explicit calls to __init__
        super(C, self).__init__()
        print("<- C")
>>> C()
-> C
-> A
-> B
<- B
<- A
<- C

在此,方法解析顺序(MRO)规定以下内容:

  • C(A, B)A首先决定,然后B。MRO是C -> A -> B -> object
  • super(A, self).__init__()沿始于的MRO链继续C.__init__进行B.__init__
  • super(B, self).__init__()沿始于的MRO链继续C.__init__进行object.__init__

您可以说这种情况是为多重继承而设计的

可访问的源代码:正确使用“旧样式”

class A(object):
    def __init__(self):
        print("-> A")
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        # Don't use super here.
        print("<- B")

class C(A, B):
    def __init__(self):
        print("-> C")
        A.__init__(self)
        B.__init__(self)
        print("<- C")
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C

在此,MRO无关紧要,因为A.__init__B.__init__被显式调用。class C(B, A):也会一样工作。

尽管这种情况不是像以前的样式那样“设计”为新样式的多重继承,但多重继承仍然是可能的。


现在,如果AB是从第三方库-即你有过的源代码没有控制AB?简短的答案:您必须设计一个实现必要super调用的适配器类,然后使用一个空类来定义MRO(请参阅Raymond Hettinger上的文章super -尤其是“如何合并非合作类”一节)。

第三方家长:A未实施superB确实

class A(object):
    def __init__(self):
        print("-> A")
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        super(B, self).__init__()
        print("<- B")

class Adapter(object):
    def __init__(self):
        print("-> C")
        A.__init__(self)
        super(Adapter, self).__init__()
        print("<- C")

class C(Adapter, B):
    pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C

Adapter实现super是为了C定义MRO,该MRO在super(Adapter, self).__init__()执行时起作用。

如果反过来呢?

第三方父母:A工具superB才不是

class A(object):
    def __init__(self):
        print("-> A")
        super(A, self).__init__()
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        print("<- B")

class Adapter(object):
    def __init__(self):
        print("-> C")
        super(Adapter, self).__init__()
        B.__init__(self)
        print("<- C")

class C(Adapter, A):
    pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C

此处的模式相同,除了执行顺序已切换Adapter.__init__super先呼叫,然后再进行显式呼叫。请注意,带有第三方父母的每种情况都需要一个唯一的适配器类。

因此,似乎除非我知道/控制我从(AB)继承的类的初始化,否则我无法对正在编写的类(C)做出安全选择。

虽然你可以处理,你没有的情况下,控制的源代码A,并B通过使用适配器类,这是事实,你必须知道在init怎样的父类实现super(如果有的话),以这样做。

Either approach (“new style” or “old style”) will work if you have control over the source code for A and B. Otherwise, use of an adapter class might be necessary.

Source code accessible: Correct use of “new style”

class A(object):
    def __init__(self):
        print("-> A")
        super(A, self).__init__()
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        super(B, self).__init__()
        print("<- B")

class C(A, B):
    def __init__(self):
        print("-> C")
        # Use super here, instead of explicit calls to __init__
        super(C, self).__init__()
        print("<- C")
>>> C()
-> C
-> A
-> B
<- B
<- A
<- C

Here, method resolution order (MRO) dictates the following:

  • C(A, B) dictates A first, then B. MRO is C -> A -> B -> object.
  • super(A, self).__init__() continues along the MRO chain initiated in C.__init__ to B.__init__.
  • super(B, self).__init__() continues along the MRO chain initiated in C.__init__ to object.__init__.

You could say that this case is designed for multiple inheritance.

Source code accessible: Correct use of “old style”

class A(object):
    def __init__(self):
        print("-> A")
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        # Don't use super here.
        print("<- B")

class C(A, B):
    def __init__(self):
        print("-> C")
        A.__init__(self)
        B.__init__(self)
        print("<- C")
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C

Here, MRO does not matter, since A.__init__ and B.__init__ are called explicitly. class C(B, A): would work just as well.

Although this case is not “designed” for multiple inheritance in the new style as the previous one was, multiple inheritance is still possible.


Now, what if A and B are from a third party library – i.e., you have no control over the source code for A and B? The short answer: You must design an adapter class that implements the necessary super calls, then use an empty class to define the MRO (see Raymond Hettinger’s article on super – especially the section, “How to Incorporate a Non-cooperative Class”).

Third-party parents: A does not implement super; B does

class A(object):
    def __init__(self):
        print("-> A")
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        super(B, self).__init__()
        print("<- B")

class Adapter(object):
    def __init__(self):
        print("-> C")
        A.__init__(self)
        super(Adapter, self).__init__()
        print("<- C")

class C(Adapter, B):
    pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C

Class Adapter implements super so that C can define the MRO, which comes into play when super(Adapter, self).__init__() is executed.

And what if it’s the other way around?

Third-party parents: A implements super; B does not

class A(object):
    def __init__(self):
        print("-> A")
        super(A, self).__init__()
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        print("<- B")

class Adapter(object):
    def __init__(self):
        print("-> C")
        super(Adapter, self).__init__()
        B.__init__(self)
        print("<- C")

class C(Adapter, A):
    pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C

Same pattern here, except the order of execution is switched in Adapter.__init__; super call first, then explicit call. Notice that each case with third-party parents requires a unique adapter class.

So it seems that unless I know/control the init’s of the classes I inherit from (A and B) I cannot make a safe choice for the class I’m writing (C).

Although you can handle the cases where you don’t control the source code of A and B by using an adapter class, it is true that you must know how the init’s of the parent classes implement super (if at all) in order to do so.


回答 3

正如雷蒙德(Raymond)在回答中所说的那样,直接调用A.__init__B.__init__可以正常工作,并且您的代码易于阅读。

但是,它不使用C和这些类之间的继承链接。利用该链接可为您提供更多的一致性,并使最终的重构更加容易且不易出错。如何执行此操作的示例:

class C(A, B):
    def __init__(self):
        print("entering c")
        for base_class in C.__bases__:  # (A, B)
             base_class.__init__(self)
        print("leaving c")

As Raymond said in his answer, a direct call to A.__init__ and B.__init__ works fine, and your code would be readable.

However, it does not use the inheritance link between C and those classes. Exploiting that link gives you more consistancy and make eventual refactorings easier and less error-prone. An example of how to do that:

class C(A, B):
    def __init__(self):
        print("entering c")
        for base_class in C.__bases__:  # (A, B)
             base_class.__init__(self)
        print("leaving c")

回答 4

本文有助于解释协作式多重继承:

http://www.artima.com/weblogs/viewpost.jsp?thread=281127

它提到了有用的方法mro(),可向您显示方法解析顺序。在你的第二个例子,当你调用superA,该super呼叫继续在MRO。顺序中的下一个类是B,这就是为什么Binit首次被调用的原因。

这是来自python官方站点的更多技术文章:

http://www.python.org/download/releases/2.3/mro/

This article helps to explain cooperative multiple inheritance:

http://www.artima.com/weblogs/viewpost.jsp?thread=281127

It mentions the useful method mro() that shows you the method resolution order. In your 2nd example, where you call super in A, the super call continues on in MRO. The next class in the order is B, this is why B‘s init is called the first time.

Here’s a more technical article from the official python site:

http://www.python.org/download/releases/2.3/mro/


回答 5

如果要从第三方库中繁衍子类类,则不会,没有盲目的方法来调用__init__实际上起作用的基类方法(或任何其他方法),而不管基类的编程方式如何。

super使编写旨在协作实现方法的类成为复杂的多重继承树的一部分成为可能,而类继承者不必知道。但是无法使用它正确地从可能使用或可能不使用的任意类中继承super

本质上,一个类是设计为使用super基类还是直接调用基类来进行子类化,是属于该类“公共接口”一部分的属性,因此应进行记录。如果您以库作者所期望的方式使用第三方库,并且库具有合理的文档,则通常会告诉您需要做什么来对特定的事物进行子类化。如果不是,那么您必须查看要子类化的类的源代码,并查看其基类调用约定是什么。如果你是从一个或多个第三方库的方式,该库作者结合多个类想到,那么它可能无法始终如一地调用超类的方法在所有; 如果类A是使用的层次结构的一部分,super而类B是不使用super的层次结构的一部分,则不能保证这两种选择都不会起作用。您将必须找出一种适用于每个特定案例的策略。

If you are multiply sub-classing classes from third party libraries, then no, there is no blind approach to calling the base class __init__ methods (or any other methods) that actually works regardless of how the base classes are programmed.

super makes it possible to write classes designed to cooperatively implement methods as part of complex multiple inheritance trees which need not be known to the class author. But there’s no way to use it to correctly inherit from arbitrary classes that may or may not use super.

Essentially, whether a class is designed to be sub-classed using super or with direct calls to the base class is a property which is part of the class’ “public interface”, and it should be documented as such. If you’re using third-party libraries in the way that the library author expected and the library has reasonable documentation, it would normally tell you what you are required to do to subclass particular things. If not, then you’ll have to look at the source code for the classes you’re sub-classing and see what their base-class-invocation convention is. If you’re combining multiple classes from one or more third-party libraries in a way that the library authors didn’t expect, then it may not be possible to consistently invoke super-class methods at all; if class A is part of a hierarchy using super and class B is part of a hierarchy that doesn’t use super, then neither option is guaranteed to work. You’ll have to figure out a strategy that happens to work for each particular case.


Python super()引发TypeError

问题:Python super()引发TypeError

在Python 2.5中,以下代码引发TypeError

>>> class X:
      def a(self):
        print "a"

>>> class Y(X):
      def a(self):
        super(Y,self).a()
        print "b"

>>> c = Y()
>>> c.a()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in a
TypeError: super() argument 1 must be type, not classobj

如果我更换class Xclass X(object),它会奏效。这有什么解释?

In Python 2.5, the following code raises a TypeError:

>>> class X:
      def a(self):
        print "a"

>>> class Y(X):
      def a(self):
        super(Y,self).a()
        print "b"

>>> c = Y()
>>> c.a()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in a
TypeError: super() argument 1 must be type, not classobj

If I replace the class X with class X(object), it will work. What’s the explanation for this?


回答 0

原因是super()只能在新型类上运行,这在2.x系列中意味着从object

>>> class X(object):
        def a(self):
            print 'a'

>>> class Y(X):
        def a(self):
            super(Y, self).a()
            print 'b'

>>> c = Y()
>>> c.a()
a
b

The reason is that super() only operates on new-style classes, which in the 2.x series means extending from object:

>>> class X(object):
        def a(self):
            print 'a'

>>> class Y(X):
        def a(self):
            super(Y, self).a()
            print 'b'

>>> c = Y()
>>> c.a()
a
b

回答 1

另外,除非必须,否则不要使用super()。您可能会怀疑,使用新型类不是通用的“正确的事情”。

有时候,您可能期望多重继承,并且可能会想要多重继承,但是在您知道MRO的繁琐细节之前,最好不要去管它,并坚持:

 X.a(self)

In addition, don’t use super() unless you have to. It’s not the general-purpose “right thing” to do with new-style classes that you might suspect.

There are times when you’re expecting multiple inheritance and you might possibly want it, but until you know the hairy details of the MRO, best leave it alone and stick to:

 X.a(self)

回答 2

如果以上答案均未明确提及。您的父类需要继承自“对象”,这实际上会将其转变为新的样式类。

# python 3.x:
class ClassName(object): # This is a new style class
    pass

class ClassName: # This is also a new style class ( implicit inheritance from object )
    pass

# Python 2.x:
class ClassName(object): # This is a new style class
    pass

class ClassName:         # This is a old style class
    pass

In case none of the above answers mentioned it clearly. Your parent class needs to inherit from “object”, which would essentially turn it into a new style class.

# python 3.x:
class ClassName(object): # This is a new style class
    pass

class ClassName: # This is also a new style class ( implicit inheritance from object )
    pass

# Python 2.x:
class ClassName(object): # This is a new style class
    pass

class ClassName:         # This is a old style class
    pass

回答 3

我尝试了各种Xa()方法;但是,它们似乎需要X的实例才能执行a(),所以我做了X()。a(self),这似乎比以前的回答更完整,至少对于我遇到的应用程序而言。由于不必要的构造和破坏,这似乎不是解决问题的好方法,但它可以正常工作。

我的特定应用程序是Python的cmd.Cmd模块,由于某种原因,它显然不是NewStyle对象。

最后结果:

X().a(self)

I tried the various X.a() methods; however, they seem to require an instance of X in order to perform a(), so I did X().a(self), which seems more complete than the previous answers, at least for the applications I’ve encountered. It doesn’t seem to be a good way of handling the problem as there is unnecessary construction and destruction, but it works fine.

My specific application was Python’s cmd.Cmd module, which is evidently not a NewStyle object for some reason.

Final Result:

X().a(self)

为什么Python 3.x的super()有魔力?

问题:为什么Python 3.x的super()有魔力?

在Python 3.x中,super()可以不带参数调用:

class A(object):
    def x(self):
         print("Hey now")

class B(A):
    def x(self):
        super().x()
>>> B().x()
Hey now

为了完成这项工作,需要执行一些编译时的魔术,其结果是以下代码(重新绑定supersuper_)失败了:

super_ = super

class A(object):
    def x(self):
        print("No flipping")

class B(A):
    def x(self):
        super_().x()
>>> B().x()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in x
RuntimeError: super(): __class__ cell not found

为什么super()在没有编译器帮助的情况下无法在运行时解析超类?在实际情况下,这种行为或其根本原因可能会咬一个粗心的程序员吗?

…,还有一个附带的问题:Python中是否还有其他函数,方法等示例,可以通过将它们重新绑定为其他名称来破坏它们吗?

In Python 3.x, super() can be called without arguments:

class A(object):
    def x(self):
         print("Hey now")

class B(A):
    def x(self):
        super().x()
>>> B().x()
Hey now

In order to make this work, some compile-time magic is performed, one consequence of which is that the following code (which rebinds super to super_) fails:

super_ = super

class A(object):
    def x(self):
        print("No flipping")

class B(A):
    def x(self):
        super_().x()
>>> B().x()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in x
RuntimeError: super(): __class__ cell not found

Why is super() unable to resolve the superclass at runtime without assistance from the compiler? Are there practical situations in which this behaviour, or the underlying reason for it, could bite an unwary programmer?

… and, as a side question: are there any other examples in Python of functions, methods etc. which can be broken by rebinding them to a different name?


回答 0

super()添加了新的魔术行为,以避免违反DRY(请勿重复自己)原理,请参阅PEP 3135。必须通过将其引用为全局来显式地命名该类,这也容易产生与您自己发现的相同的重新绑定问题super()

class Foo(Bar):
    def baz(self):
        return super(Foo, self).baz() + 42

Spam = Foo
Foo = something_else()

Spam().baz()  # liable to blow up

对于使用类装饰器的情况也是如此,其中装饰器返回一个新对象,该对象重新绑定了类名:

@class_decorator_returning_new_class
class Foo(Bar):
    def baz(self):
        # Now `Foo` is a *different class*
        return super(Foo, self).baz() + 42

魔术 super() __class__单元使您可以访问原始类对象,从而很好地避开了这些问题。

PEP由Guido开始,他最初设想super成为关键字,并且使用单元格查找当前Class的想法也是他的想法。当然,使其成为关键字的想法是PEP初稿的一部分

但是,实际上是Guido自己才放弃了“太不可思议”的关键字想法,而是提出了当前的实现方式。他预计使用其他名称super()可能会出现问题

我的补丁使用了一种中间解决方案:假定您__class__ 每次使用名为的变量时都需要使用该解决方案'super'。因此,如果你(全局)重新命名super,以supper和使用supper,但不能super,也不会不带参数的工作(但它仍然可以工作,如果你通过它要么 __class__或实际的类对象); 如果您有一个名为的不相关变量super,那么一切都会起作用,但是该方法将使用稍慢一些的用于单元变量的调用路径。

因此,最终,Guido自己宣布使用super关键字感觉不正确,并且提供一个魔术__class__单元是可以接受的折衷方案。

我同意该实现的神奇的,隐式的行为有些令人惊讶,但它super()是该语言中使用最广泛的功能之一。只需看看 Internet上所有被误用super(type(self), self)super(self.__class__, self)调用的内容;如果从派生类调用了任何代码,最终都会遇到无限递归异常。至少super(),没有参数的简化调用避免了这种情况问题。

至于更名super_; 刚才提到__class__您的方法,以及它会重新工作。如果您在方法中引用super __class__名称,则会创建该单元格:

>>> super_ = super
>>> class A(object):
...     def x(self):
...         print("No flipping")
... 
>>> class B(A):
...     def x(self):
...         __class__  # just referencing it is enough
...         super_().x()
... 
>>> B().x()
No flipping

The new magic super() behaviour was added to avoid violating the D.R.Y. (Don’t Repeat Yourself) principle, see PEP 3135. Having to explicitly name the class by referencing it as a global is also prone to the same rebinding issues you discovered with super() itself:

class Foo(Bar):
    def baz(self):
        return super(Foo, self).baz() + 42

Spam = Foo
Foo = something_else()

Spam().baz()  # liable to blow up

The same applies to using class decorators where the decorator returns a new object, which rebinds the class name:

@class_decorator_returning_new_class
class Foo(Bar):
    def baz(self):
        # Now `Foo` is a *different class*
        return super(Foo, self).baz() + 42

The magic super() __class__ cell sidesteps these issues nicely by giving you access to the original class object.

The PEP was kicked off by Guido, who initially envisioned super becoming a keyword, and the idea of using a cell to look up the current class was also his. Certainly, the idea to make it a keyword was part of the first draft of the PEP.

However, it was in fact Guido himself who then stepped away from the keyword idea as ‘too magical’, proposing the current implementation instead. He anticipated that using a different name for super() could be a problem:

My patch uses an intermediate solution: it assumes you need __class__ whenever you use a variable named 'super'. Thus, if you (globally) rename super to supper and use supper but not super, it won’t work without arguments (but it will still work if you pass it either __class__ or the actual class object); if you have an unrelated variable named super, things will work but the method will use the slightly slower call path used for cell variables.

So, in the end, it was Guido himself that proclaimed that using a super keyword did not feel right, and that providing a magic __class__ cell was an acceptable compromise.

I agree that the magic, implicit behaviour of the implementation is somewhat surprising, but super() is one of the most mis-applied functions in the language. Just take a look at all the misapplied super(type(self), self) or super(self.__class__, self) invocations found on the Internet; if any of that code was ever called from a derived class you’d end up with an infinite recursion exception. At the very least the simplified super() call, without arguments, avoids that problem.

As for the renamed super_; just reference __class__ in your method as well and it’ll work again. The cell is created if you reference either the super or __class__ names in your method:

>>> super_ = super
>>> class A(object):
...     def x(self):
...         print("No flipping")
... 
>>> class B(A):
...     def x(self):
...         __class__  # just referencing it is enough
...         super_().x()
... 
>>> B().x()
No flipping

super()失败,并显示错误:当父级未从对象继承时,TypeError“参数1必须为类型,而不是classobj”

问题:super()失败,并显示错误:当父级未从对象继承时,TypeError“参数1必须为类型,而不是classobj”

我收到一些我不知道的错误。任何线索我的示例代码有什么问题吗?

class B:
    def meth(self, arg):
        print arg

class C(B):
    def meth(self, arg):
        super(C, self).meth(arg)

print C().meth(1)

我从“ super”内置方法的帮助下获得了示例测试代码。

这是错误:

Traceback (most recent call last):
  File "./test.py", line 10, in ?
    print C().meth(1)
  File "./test.py", line 8, in meth
    super(C, self).meth(arg)
TypeError: super() argument 1 must be type, not classobj

仅供参考,这是python本身的帮助(超级):

Help on class super in module __builtin__:

class super(object)
 |  super(type) -> unbound super object
 |  super(type, obj) -> bound super object; requires isinstance(obj, type)
 |  super(type, type2) -> bound super object; requires issubclass(type2, type)
 |  Typical use to call a cooperative superclass method:
 |  class C(B):
 |      def meth(self, arg):
 |          super(C, self).meth(arg)
 |

I get some error that I can’t figure out. Any clue what is wrong with my sample code?

class B:
    def meth(self, arg):
        print arg

class C(B):
    def meth(self, arg):
        super(C, self).meth(arg)

print C().meth(1)

I got the sample test code from help of ‘super’ built-in method.

Here is the error:

Traceback (most recent call last):
  File "./test.py", line 10, in ?
    print C().meth(1)
  File "./test.py", line 8, in meth
    super(C, self).meth(arg)
TypeError: super() argument 1 must be type, not classobj

FYI, here is the help(super) from python itself:

Help on class super in module __builtin__:

class super(object)
 |  super(type) -> unbound super object
 |  super(type, obj) -> bound super object; requires isinstance(obj, type)
 |  super(type, type2) -> bound super object; requires issubclass(type2, type)
 |  Typical use to call a cooperative superclass method:
 |  class C(B):
 |      def meth(self, arg):
 |          super(C, self).meth(arg)
 |

回答 0

您的问题是类B没有声明为“新式”类。像这样更改它:

class B(object):

它会工作。

super()并且所有子类/超类的内容仅适用于新型类。我建议您养成始终(object)在任何类定义上键入它的习惯,以确保它是一种新型的类。

旧式类(也称为“经典”类)始终为type classobj;新样式类的类型为type。这就是为什么您看到错误消息的原因:

TypeError: super() argument 1 must be type, not classobj

试试看自己:

class OldStyle:
    pass

class NewStyle(object):
    pass

print type(OldStyle)  # prints: <type 'classobj'>

print type(NewStyle) # prints <type 'type'>

请注意,在Python 3.x中,所有类都是新样式。您仍然可以使用旧样式类中的语法,但是会获得新样式类。因此,在Python 3.x中,您将不会遇到此问题。

Your problem is that class B is not declared as a “new-style” class. Change it like so:

class B(object):

and it will work.

super() and all subclass/superclass stuff only works with new-style classes. I recommend you get in the habit of always typing that (object) on any class definition to make sure it is a new-style class.

Old-style classes (also known as “classic” classes) are always of type classobj; new-style classes are of type type. This is why you got the error message you saw:

TypeError: super() argument 1 must be type, not classobj

Try this to see for yourself:

class OldStyle:
    pass

class NewStyle(object):
    pass

print type(OldStyle)  # prints: <type 'classobj'>

print type(NewStyle) # prints <type 'type'>

Note that in Python 3.x, all classes are new-style. You can still use the syntax from the old-style classes but you get a new-style class. So, in Python 3.x you won’t have this problem.


回答 1

另外,如果您不能更改类B,则可以使用多重继承来修复错误。

class B:
    def meth(self, arg):
        print arg

class C(B, object):
    def meth(self, arg):
        super(C, self).meth(arg)

print C().meth(1)

Also, if you can’t change class B, you can fix the error by using multiple inheritance.

class B:
    def meth(self, arg):
        print arg

class C(B, object):
    def meth(self, arg):
        super(C, self).meth(arg)

print C().meth(1)

回答 2

如果python版本是3.X,就可以了。

我认为您的python版本是2.X,在添加此代码时,超级将可用

__metaclass__ = type

所以代码是

__metaclass__ = type
class B:
    def meth(self, arg):
        print arg
class C(B):
    def meth(self, arg):
        super(C, self).meth(arg)
print C().meth(1)

If the python version is 3.X, it’s okay.

I think your python version is 2.X, the super would work when adding this code

__metaclass__ = type

so the code is

__metaclass__ = type
class B:
    def meth(self, arg):
        print arg
class C(B):
    def meth(self, arg):
        super(C, self).meth(arg)
print C().meth(1)

回答 3

当我使用python 2.7时,也会遇到发布的问题。它在python 3.4下工作得很好

为了使其在python 2.7中工作,我__metaclass__ = type在程序顶部添加了该属性,并且该属性可以正常工作。

__metaclass__ :简化了从旧样式类到新样式类的过渡。

I was also faced by the posted issue when I used python 2.7. It is working very fine with python 3.4

To make it work in python 2.7 I have added the __metaclass__ = type attribute at the top of my program and it worked.

__metaclass__ : It eases the transition from old-style classes and new-style classes.


对于新样式的类,super()引发“ TypeError:必须为类型,而不是classobj”

问题:对于新样式的类,super()引发“ TypeError:必须为类型,而不是classobj”

以下用法super()引发TypeError:为什么?

>>> from  HTMLParser import HTMLParser
>>> class TextParser(HTMLParser):
...     def __init__(self):
...         super(TextParser, self).__init__()
...         self.all_data = []
...         
>>> TextParser()
(...)
TypeError: must be type, not classobj

在StackOverflow上有一个类似的问题:Python super()引发TypeError,该错误由用户类不是新型类的事实来解释。但是,上面的类是一种新式的类,因为它继承自object

>>> isinstance(HTMLParser(), object)
True

我想念什么?我如何super()在这里使用?

使用HTMLParser.__init__(self)代替super(TextParser, self).__init__()可以工作,但是我想了解TypeError。

PS:Joachim指出,成为一个新类实例并不等同于成为一个实例object。我读了很多相反的书,因此感到困惑(基于object实例测试的新型类实例测试的示例:https : //stackoverflow.com/revisions/2655651/3)。

The following use of super() raises a TypeError: why?

>>> from  HTMLParser import HTMLParser
>>> class TextParser(HTMLParser):
...     def __init__(self):
...         super(TextParser, self).__init__()
...         self.all_data = []
...         
>>> TextParser()
(...)
TypeError: must be type, not classobj

There is a similar question on StackOverflow: Python super() raises TypeError, where the error is explained by the fact that the user class is not a new-style class. However, the class above is a new-style class, as it inherits from object:

>>> isinstance(HTMLParser(), object)
True

What am I missing? How can I use super(), here?

Using HTMLParser.__init__(self) instead of super(TextParser, self).__init__() would work, but I would like to understand the TypeError.

PS: Joachim pointed out that being a new-style-class instance is not equivalent to being an object. I read the opposite many times, hence my confusion (example of new-style class instance test based on object instance test: https://stackoverflow.com/revisions/2655651/3).


回答 0

好吧,这是通常的“ super()不能与老式类一起使用”。

但是,重要的一点是对“这是一个新的实例(即对象)吗?” 的正确测试。是

>>> class OldStyle: pass
>>> instance = OldStyle()
>>> issubclass(instance.__class__, object)
False

而不是(如问题所示):

>>> isinstance(instance, object)
True

对于,正确的“这是新型类”测试是:

>>> issubclass(OldStyle, object)  # OldStyle is not a new-style class
False
>>> issubclass(int, object)  # int is a new-style class
True

关键的一点是,与老式类的的实例和它的类型是不同的。在这里,OldStyle().__class__is OldStyle,它不继承自object,而type(OldStyle())is instance类型,它确实继承自object。基本上,旧式类仅创建类型的对象instance(而新式类将创建类型为类本身的对象)。这大概就是为什么实例OldStyle()object:其type()从继承object(事实上,它的类并没有继承object不计数:老式类只是构建类型的新对象instance)。部分参考:https://stackoverflow.com/a/9699961/42973

PS:新式类和旧式类之间的区别还可以通过以下方式看到:

>>> type(OldStyle)  # OldStyle creates objects but is not itself a type
classobj
>>> isinstance(OldStyle, type)
False
>>> type(int)  # A new-style class is a type
type

(旧式类不是类型,因此它们不能是其实例的类型)。

Alright, it’s the usual “super() cannot be used with an old-style class”.

However, the important point is that the correct test for “is this a new-style instance (i.e. object)?” is

>>> class OldStyle: pass
>>> instance = OldStyle()
>>> issubclass(instance.__class__, object)
False

and not (as in the question):

>>> isinstance(instance, object)
True

For classes, the correct “is this a new-style class” test is:

>>> issubclass(OldStyle, object)  # OldStyle is not a new-style class
False
>>> issubclass(int, object)  # int is a new-style class
True

The crucial point is that with old-style classes, the class of an instance and its type are distinct. Here, OldStyle().__class__ is OldStyle, which does not inherit from object, while type(OldStyle()) is the instance type, which does inherit from object. Basically, an old-style class just creates objects of type instance (whereas a new-style class creates objects whose type is the class itself). This is probably why the instance OldStyle() is an object: its type() inherits from object (the fact that its class does not inherit from object does not count: old-style classes merely construct new objects of type instance). Partial reference: https://stackoverflow.com/a/9699961/42973.

PS: The difference between a new-style class and an old-style one can also be seen with:

>>> type(OldStyle)  # OldStyle creates objects but is not itself a type
classobj
>>> isinstance(OldStyle, type)
False
>>> type(int)  # A new-style class is a type
type

(old-style classes are not types, so they cannot be the type of their instances).


回答 1

super()仅可用于新型类,这意味着根类需要从’object’类继承。

例如,顶级类需要像这样:

class SomeClass(object):
    def __init__(self):
        ....

class SomeClass():
    def __init__(self):
        ....

因此,解决方案是直接调用父级的init方法,如下所示:

class TextParser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.all_data = []

super() can be used only in the new-style classes, which means the root class needs to inherit from the ‘object’ class.

For example, the top class need to be like this:

class SomeClass(object):
    def __init__(self):
        ....

not

class SomeClass():
    def __init__(self):
        ....

So, the solution is that call the parent’s init method directly, like this way:

class TextParser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.all_data = []

回答 2

您也可以使用class TextParser(HTMLParser, object):。这将创建TextParser一个新样式的类,并且super()可以使用。

You can also use class TextParser(HTMLParser, object):. This makes TextParser a new-style class, and super() can be used.


回答 3

问题是super需要object一个祖先:

>>> class oldstyle:
...     def __init__(self): self.os = True

>>> class myclass(oldstyle):
...     def __init__(self): super(myclass, self).__init__()

>>> myclass()
TypeError: must be type, not classobj

经过仔细检查,发现:

>>> type(myclass)
classobj

但:

>>> class newstyle(object): pass

>>> type(newstyle)
type    

因此,解决您的问题的方法是从对象以及HTMLParser继承。但是确保对象在MRO类中排在最后:

>>> class myclass(oldstyle, object):
...     def __init__(self): super(myclass, self).__init__()

>>> myclass().os
True

The problem is that super needs an object as an ancestor:

>>> class oldstyle:
...     def __init__(self): self.os = True

>>> class myclass(oldstyle):
...     def __init__(self): super(myclass, self).__init__()

>>> myclass()
TypeError: must be type, not classobj

On closer examination one finds:

>>> type(myclass)
classobj

But:

>>> class newstyle(object): pass

>>> type(newstyle)
type    

So the solution to your problem would be to inherit from object as well as from HTMLParser. But make sure object comes last in the classes MRO:

>>> class myclass(oldstyle, object):
...     def __init__(self): super(myclass, self).__init__()

>>> myclass().os
True

回答 4

如果您查看继承树(在2.6版中),则HTMLParser继承自SGMLParser,继承自ParserBase而不继承自object。即HTMLParser是一个老式的类。

关于您的检查isinstance,我在ipython中进行了快速测试:

在[1]中:A类:
   ...:通过
   ...: 

在[2]中:isinstance(A,object)
出[2]:是

即使一个类是老式类,它仍然是的一个实例object

If you look at the inheritance tree (in version 2.6), HTMLParser inherits from SGMLParser which inherits from ParserBase which doesn’t inherits from object. I.e. HTMLParser is an old-style class.

About your checking with isinstance, I did a quick test in ipython:

In [1]: class A:
   ...:     pass
   ...: 

In [2]: isinstance(A, object)
Out[2]: True

Even if a class is old-style class, it’s still an instance of object.


回答 5

正确的方法是在不继承自’object’的旧类中执行以下操作

class A:
    def foo(self):
        return "Hi there"

class B(A):
    def foo(self, name):
        return A.foo(self) + name

the correct way to do will be as following in the old-style classes which doesn’t inherit from ‘object’

class A:
    def foo(self):
        return "Hi there"

class B(A):
    def foo(self, name):
        return A.foo(self) + name

回答 6

FWIW,尽管我不是Python专家,但我对此很满意

>>> class TextParser(HTMLParser):
...    def handle_starttag(self, tag, attrs):
...        if tag == "b":
...            self.all_data.append("bold")
...        else:
...            self.all_data.append("other")
...     
...         
>>> p = TextParser()
>>> p.all_data = []
>>> p.feed(text)
>>> print p.all_data
(...)

只是让我根据需要返回解析结果。

FWIW and though I’m no Python guru I got by with this

>>> class TextParser(HTMLParser):
...    def handle_starttag(self, tag, attrs):
...        if tag == "b":
...            self.all_data.append("bold")
...        else:
...            self.all_data.append("other")
...     
...         
>>> p = TextParser()
>>> p.all_data = []
>>> p.feed(text)
>>> print p.all_data
(...)

Just got me the parse results back as needed.


“ super”在Python中做什么?

问题:“ 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.

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.