关于继承:Python扩展 – 使用super()Python 3与Python 2

Python extending with - using super() Python 3 vs Python 2

最初我想问这个问题,但后来我发现它已经被人想到了…

在谷歌搜索中,我发现了这个扩展configparser的例子。以下内容适用于python 3:

1
2
3
4
5
6
7
8
9
$ 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:

1
2
3
4
5
6
7
8
9
>>> 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新类和旧类样式的文章(例如这里)。现在我想知道,我可以做到:

1
2
3
4
5
6
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中,这是否等同于:

1
2
3
4
5
6
7
 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)


  • python 3中引入了super()(没有参数)(与__class__):

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

    所以这将是新样式类的python 2等价物:

    1
    super(CurrentClass, self)
  • 对于旧样式类,可以始终使用:

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


在单个继承情况下(仅当您子类一个类时),您的新类继承基类的方法。这包括__init__。所以如果你不在你的类中定义它,你将从基础中得到一个。

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

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

如果是这样的话,那么这也可以正常工作(在python 3中,但是您可以将其改写为python 2——它也有super):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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(方法解析顺序)中的下一个类调用方法。C的MRO是:(C, A, B, object)。您可以打印C.__mro__来查看它。

因此,CA继承__init__A.__init__中的super在mro中称B.__init__(B跟随A

因此,在C中什么都不做,最终你会打电话给他们,这是你想要的。

如果你不使用super,你最终会继承A.__init__(和以前一样),但这次没有什么可以称为B.__init__的了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A:
    def __init__(self):
        print('A')

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

class C(A, B):
    pass

C()
#prints:
#A

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

1
2
3
4
class C(A, B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)

问题在于,在更复杂的MI树中,某些类的__init__方法可能会被多次调用,而super/mro保证它们只被调用一次。


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

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

1
2
3
    class MySubClass(MySuperClass):
        def __init__(self):
            MySuperClass.__init__(self)

(2)使代码更抽象(更可移植)。一种获得超级类的常用方法被发明如下:

1
    super(<class>, <instance>)

init函数可以是:

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

但是,如果需要类和实例的显式传递,就需要打破一点干涸(不要重复自己)规则。

(3)V3。它更聪明,

1
    super()

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


只需为python 3提供一个简单而完整的示例,大多数人现在似乎都在使用它。

1
2
3
4
5
6
7
8
9
10
11
12
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)

给予

1
2
42
chickenman

另一个python3实现涉及使用带有super()的抽象类。你应该记住

super().init(name, 10)

效果与

Person.init(self, name, 10)

记住super()中有一个隐藏的"self",所以同一个对象传递给superclass in it方法,属性被添加到调用它的对象中。因此,super()被翻译成Person,然后如果包含隐藏的自我,就得到上面的代码片段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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())