Python扩展-使用super()Python 3 vs Python 2

问题:Python扩展-使用super()Python 3 vs Python 2

本来我想问这个问题,但是后来我发现它已经被想到了……

在谷歌搜索中发现了扩展configparser的示例。以下适用于Python 3:

$ python3
Python 3.2.3rc2 (default, Mar 21 2012, 06:59:51) 
[GCC 4.6.3] on linux2
>>> from configparser import  SafeConfigParser
>>> class AmritaConfigParser(SafeConfigParser):
...     def __init_(self):
...         super().__init__()
... 
>>> cfg = AmritaConfigParser()

但不适用于Python 2:

>>> class AmritaConfigParser(SafeConfigParser):
...       def __init__(self):
...           super(SafeConfigParser).init()
... 
>>> cfg = AmritaConfigParser()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
TypeError: must be type, not classob

然后,我读了一些关于Python New Class vs. Old Class样式的信息(例如,在这里。现在我想知道,我可以这样做:

class MyConfigParser(ConfigParser.ConfigParser):
      def Write(self, fp):
          """override the module's original write funcition"""
          ....
      def MyWrite(self, fp):
          """Define new function and inherit all others"""

但是,我不应该叫init吗?这在Python 2中是否等效:

 class AmritaConfigParser(ConfigParser.SafeConfigParser):
    #def __init__(self):
    #    super().__init__() # Python3 syntax, or rather, new style class syntax ...
    #
    # is this the equivalent of the above ? 
    def __init__(self):
        ConfigParser.SafeConfigParser.__init__(self)

Originally I wanted to ask this question, but then I found it was already thought of before…

Googling around I found this example of extending configparser. The following works with Python 3:

$ python3
Python 3.2.3rc2 (default, Mar 21 2012, 06:59:51) 
[GCC 4.6.3] on linux2
>>> from configparser import  SafeConfigParser
>>> class AmritaConfigParser(SafeConfigParser):
...     def __init_(self):
...         super().__init__()
... 
>>> cfg = AmritaConfigParser()

But not with Python 2:

>>> class AmritaConfigParser(SafeConfigParser):
...       def __init__(self):
...           super(SafeConfigParser).init()
... 
>>> cfg = AmritaConfigParser()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
TypeError: must be type, not classob

Then I read a little bit on Python New Class vs. Old Class styles (e.g. here. And now I am wondering, I can do:

class MyConfigParser(ConfigParser.ConfigParser):
      def Write(self, fp):
          """override the module's original write funcition"""
          ....
      def MyWrite(self, fp):
          """Define new function and inherit all others"""

But, shouldn’t I call init? Is this in Python 2 the equivalent:

 class AmritaConfigParser(ConfigParser.SafeConfigParser):
    #def __init__(self):
    #    super().__init__() # Python3 syntax, or rather, new style class syntax ...
    #
    # is this the equivalent of the above ? 
    def __init__(self):
        ConfigParser.SafeConfigParser.__init__(self)

回答 0

  • super()(不带参数)在Python 3中(以及__class__)引入:

    super() -> same as super(__class__, self)

    这样就相当于新样式类的Python 2:

    super(CurrentClass, self)
  • 对于老式类,您可以始终使用:

     class Classname(OldStyleParent):
        def __init__(self, *args, **kwargs):
            OldStyleParent.__init__(self, *args, **kwargs)
  • super() (without arguments) was introduced in Python 3 (along with __class__):

    super() -> same as super(__class__, self)
    

    so that would be the Python 2 equivalent for new-style classes:

    super(CurrentClass, self)
    
  • for old-style classes you can always use:

     class Classname(OldStyleParent):
        def __init__(self, *args, **kwargs):
            OldStyleParent.__init__(self, *args, **kwargs)
    

回答 1

在单个继承的情况下(仅当子类化一个类时),新类将继承基类的方法。这包括__init__。因此,如果您不在课堂上定义它,那么您将从基础中获得一个。

如果引入多重继承(一次子类化多个类),事情就会变得复杂起来。这是因为如果有多个基类__init__,则您的类将仅继承第一个基类。

在这种情况下,super如果可以的话,您应该真正使用,我会解释原因。但并非总是可以。问题是您所有的基类也必须使用它(以及它们的基类-整个树)。

如果是这种情况,那么这也将正常工作(在Python 3中,但您可以将其重新制作为Python 2-它还具有super):

class A:
    def __init__(self):
        print('A')
        super().__init__()

class B:
    def __init__(self):
        print('B')
        super().__init__()

class C(A, B):
    pass

C()
#prints:
#A
#B

请注意,super即使它们没有自己的基类,这两个基类也如何使用。

什么super做的是:它要求从隔壁班的MRO方法(方法解析顺序)。的MRO为C(C, A, B, object)。您可以打印C.__mro__以查看它。

因此,C继承__init__自调用AsuperA.__init__调用中继承B.__init__(在MRO中B遵循A)。

因此,通过不执行任何操作C,最终会同时调用这两者,而这正是您想要的。

现在,如果您不使用super,您将最终继承A.__init__(与以前一样),但是这次没有什么B.__init__需要您使用。

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

class B:
    def __init__(self):
        print('B')

class C(A, B):
    pass

C()
#prints:
#A

要解决此问题,您必须定义C.__init__

class C(A, B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)

这样做的问题是,在更复杂的MI树中,__init__某些类的方法最终可能会被多次调用,而super / MRO保证只将它们调用一次。

In a single inheritance case (when you subclass one class only), your new class inherits methods of the base class. This includes __init__. So if you don’t define it in your class, you will get the one from the base.

Things start being complicated if you introduce multiple inheritance (subclassing more than one class at a time). This is because if more than one base class has __init__, your class will inherit the first one only.

In such cases, you should really use super if you can, I’ll explain why. But not always you can. The problem is that all your base classes must also use it (and their base classes as well — the whole tree).

If that is the case, then this will also work correctly (in Python 3 but you could rework it into Python 2 — it also has super):

class A:
    def __init__(self):
        print('A')
        super().__init__()

class B:
    def __init__(self):
        print('B')
        super().__init__()

class C(A, B):
    pass

C()
#prints:
#A
#B

Notice how both base classes use super even though they don’t have their own base classes.

What super does is: it calls the method from the next class in MRO (method resolution order). The MRO for C is: (C, A, B, object). You can print C.__mro__ to see it.

So, C inherits __init__ from A and super in A.__init__ calls B.__init__ (B follows A in MRO).

So by doing nothing in C, you end up calling both, which is what you want.

Now if you were not using super, you would end up inheriting A.__init__ (as before) but this time there’s nothing that would call B.__init__ for you.

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

class B:
    def __init__(self):
        print('B')

class C(A, B):
    pass

C()
#prints:
#A

To fix that you have to define C.__init__:

class C(A, B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)

The problem with that is that in more complicated MI trees, __init__ methods of some classes may end up being called more than once whereas super/MRO guarantee that they’re called just once.


回答 2

简而言之,它们是等效的。让我们来看看历史:

(1)首先,函数看起来像这样。

    class MySubClass(MySuperClass):
        def __init__(self):
            MySuperClass.__init__(self)

(2)使代码更抽象(更便于移植)。发明超类的一种常见方法是:

    super(<class>, <instance>)

初始化函数可以是:

    class MySubClassBetter(MySuperClass):
        def __init__(self):
            super(MySubClassBetter, self).__init__()

但是,要求显式传递类和实例都违反了DRY(请勿重复自己)规则。

(3)在V3中。更聪明

    super()

在大多数情况下就足够了。您可以参考http://www.python.org/dev/peps/pep-3135/

In short, they are equivalent. Let’s have a history view:

(1) at first, the function looks like this.

    class MySubClass(MySuperClass):
        def __init__(self):
            MySuperClass.__init__(self)

(2) to make code more abstract (and more portable). A common method to get Super-Class is invented like:

    super(<class>, <instance>)

And init function can be:

    class MySubClassBetter(MySuperClass):
        def __init__(self):
            super(MySubClassBetter, self).__init__()

However requiring an explicit passing of both the class and instance break the DRY (Don’t Repeat Yourself) rule a bit.

(3) in V3. It is more smart,

    super()

is enough in most case. You can refer to http://www.python.org/dev/peps/pep-3135/


回答 3

只是为Python 3提供一个简单而完整的示例,大多数人似乎正在使用它。

class MySuper(object):
    def __init__(self,a):
        self.a = a

class MySub(MySuper):
    def __init__(self,a,b):
        self.b = b
        super().__init__(a)

my_sub = MySub(42,'chickenman')
print(my_sub.a)
print(my_sub.b)

42
chickenman

Just to have a simple and complete example for Python 3, which most people seem to be using now.

class MySuper(object):
    def __init__(self,a):
        self.a = a

class MySub(MySuper):
    def __init__(self,a,b):
        self.b = b
        super().__init__(a)

my_sub = MySub(42,'chickenman')
print(my_sub.a)
print(my_sub.b)

gives

42
chickenman

回答 4

另一个python3实现,其中涉及将Abstract类与super()结合使用。你应该记住

super().__init__(name, 10)

与…具有相同的效果

Person.__init__(self, name, 10)

请记住,super()中有一个隐藏的“自我”,因此同一对象会传递给超类init方法,并且属性会添加到调用它的对象中。因此super()被翻译为 Person,然后如果您包含隐藏的自身,则会得到上面的代码片段。

from abc import ABCMeta, abstractmethod
class Person(metaclass=ABCMeta):
    name = ""
    age = 0

    def __init__(self, personName, personAge):
        self.name = personName
        self.age = personAge

    @abstractmethod
    def showName(self):
        pass

    @abstractmethod
    def showAge(self):
        pass


class Man(Person):

    def __init__(self, name, height):
        self.height = height
        # Person.__init__(self, name, 10)
        super().__init__(name, 10)  # same as Person.__init__(self, name, 10)
        # basically used to call the superclass init . This is used incase you want to call subclass init
        # and then also call superclass's init.
        # Since there's a hidden self in the super's parameters, when it's is called,
        # the superclasses attributes are a part of the same object that was sent out in the super() method

    def showIdentity(self):
        return self.name, self.age, self.height

    def showName(self):
        pass

    def showAge(self):
        pass


a = Man("piyush", "179")
print(a.showIdentity())

Another python3 implementation that involves the use of Abstract classes with super(). You should remember that

super().__init__(name, 10)

has the same effect as

Person.__init__(self, name, 10)

Remember there’s a hidden ‘self’ in super(), So the same object passes on to the superclass init method and the attributes are added to the object that called it. Hence super()gets translated to Person and then if you include the hidden self, you get the above code frag.

from abc import ABCMeta, abstractmethod
class Person(metaclass=ABCMeta):
    name = ""
    age = 0

    def __init__(self, personName, personAge):
        self.name = personName
        self.age = personAge

    @abstractmethod
    def showName(self):
        pass

    @abstractmethod
    def showAge(self):
        pass


class Man(Person):

    def __init__(self, name, height):
        self.height = height
        # Person.__init__(self, name, 10)
        super().__init__(name, 10)  # same as Person.__init__(self, name, 10)
        # basically used to call the superclass init . This is used incase you want to call subclass init
        # and then also call superclass's init.
        # Since there's a hidden self in the super's parameters, when it's is called,
        # the superclasses attributes are a part of the same object that was sent out in the super() method

    def showIdentity(self):
        return self.name, self.age, self.height

    def showName(self):
        pass

    def showAge(self):
        pass


a = Man("piyush", "179")
print(a.showIdentity())