彻底搞懂python super函数的作用 |
您所在的位置:网站首页 › 在java中super作用 › 彻底搞懂python super函数的作用 |
super() 的入门使用
在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我们就需要调用父类的方法了。 调用父类同名方法有两种方式: 1、调用未绑定的父类方法 2、使用super函数来调用 先来说下第一种方式:调用未绑定的父类方法。 演示: class Base(object): def greet(self): print('hi,I am Base') class A(Base): def greet(self): Base.greet(self) #通过父类Base直接调用greet方法,并把self作为参数 print('hi,I am A') a=A() a.greet()运行结果: C:\python36\python.exe E:/demo/testPyQt.py hi,I am Base hi,I am A Process finished with exit code 0这种方式简单用还可以,如果在多重继承中就会有问题。 演示: class Base(object): def __init__(self): print("enter Base") print("leave Base") class A(Base): def __init__(self): print("enter A") Base.__init__(self) #调用父类的构造函数进行初始化 print("leave A") class B(Base): def __init__(self): print("enter B") Base.__init__(self) #调用父类的构造函数进行初始化 print("leave B") class C(A,B): def __init__(self): print("enter C") A.__init__(self) #调用父类A的构造函数进行初始化 B.__init__(self) #调用父类B的构造函数进行初始化 print("leave C") c=C()运行结果: C:\python36\python.exe E:/demo/testPyQt.py enter C enter A enter Base leave Base leave A enter B enter Base leave Base leave B leave C Process finished with exit code 0从上面的运行结果可以看出,基类Base的构造函数被调用了两次,这是有问题的,正常的应该是:A的构造函数调用一次,B的构造函数调用一次,基类Base的构造函数调用一次。 那么这个问题能不能解决呢,可以的,下面介绍super。 演示: class Animal(object): def __init__(self, name): self.name = name def greet(self): print('Hello, I am %s.' % self.name) class Dog(Animal): def greet(self): super(Dog, self).greet() #调用父类Animal的greet方法 print('WangWang...') d=Dog("xiaohuang") d.greet()在上面,Animal 是父类,Dog 是子类,我们在 Dog 类重定义了 greet 方法,为了能同时实现父类的功能,我们又调用了父类的方法,运行结果: C:\python36\python.exe E:/demo/testPyQt.py Hello, I am xiaohuang. WangWang... Process finished with exit code 0super 的一个最常见用法可以说是在子类中调用父类的初始化方法了。 演示: class Base(object): def __init__(self, a, b): self.a = a self.b = b class A(Base): def __init__(self, a, b, c): super(A, self).__init__(a, b) # Python3 可使用 super().__init__(a, b) self.c = c a=A(100,200,300) print("a=%d, b=%d, c=%d" % (a.a,a.b,a.c))运行结果: C:\python36\python.exe E:/demo/testPyQt.py a=100, b=200, c=300 Process finished with exit code 0 深入super()看了上面的使用,你可能会觉得 super 的使用很简单,无非就是获取了父类,并调用父类的方法。其实,在上面的情况下,super 获得的类刚好是父类,但在其他情况就不一定了,super 其实和父类没有实质性的关联。 让我们看一个稍微复杂的例子,涉及到多重继承,代码如下: class Base(object): def __init__(self): print("enter Base") print("leave Base") class A(Base): def __init__(self): print("enter A") super(A,self).__init__() print("leave A") class B(Base): def __init__(self): print("enter B") super(B,self).__init__() print("leave B") class C(A,B): def __init__(self): print("enter C") super(C,self).__init__() print("leave C") c=C()运行结果: C:\python36\python.exe E:/demo/testPyQt.py enter C enter A enter B enter Base leave Base leave B leave A leave C Process finished with exit code 0OK,这个运行结果是正常的,类C继承自A,B,而A和B又分别继承类Base,每一个类的构造函数分别被调用了一次。 如果你认为 super 代表『调用父类的方法』,那你很可能会疑惑为什么 enter A 的下一句不是 enter Base 而是 enter B。原因是,super 和父类没有实质性的关联,现在让我们搞清 super 是怎么运作的。 MRO列表事实上,对于你定义的每一个类,Python 会计算出一个方法解析顺序(Method Resolution Order, MRO)列表,它代表了类继承的顺序,我们可以使用下面的方式获得某个类的 MRO 列表: print(C.mro())运行结果: [, , , , ]这个列表真实的列出了类C的继承顺序。C->A->B->Base->object。在方法调用时,是按照这个顺序查找的。 那这个 MRO 列表的顺序是怎么定的呢,它是通过一个 C3 线性化算法来实现的,这里我们就不去深究这个算法了,感兴趣的读者可以自己去了解一下,总的来说,一个类的 MRO 列表就是合并所有父类的 MRO 列表,并遵循以下三条原则: 子类永远在父类前面如果有多个父类,会根据它们在列表中的顺序被检查如果对下一个类存在两个合法的选择,选择第一个父类 super 原理super 的工作原理如下: def super(cls, inst): mro = inst.__class__.mro() return mro[mro.index(cls) + 1]其中,cls 代表类,inst 代表实例,上面的代码做了两件事: 获取 inst 的 MRO 列表查找 cls 在当前 MRO 列表中的 index, 并返回它的下一个类,即 mro[index + 1]当你使用 super(cls, inst) 时,Python 会在 inst 的 MRO 列表上搜索 cls 的下一个类。 现在,让我们回到前面的例子。 首先看类 C 的 init 方法: super(C,self).__init__()这里的 self 是当前 C 的实例,self.class.mro() 结果是: [, , , , ]可以看到,C 的下一个类是 A,于是,跳到了 A 的 init,这时会打印出 enter A,并执行下面一行代码: super(A,self).__init__()注意,这里的 self 也是当前 C 的实例,MRO 列表跟上面是一样的,搜索 A 在 MRO 中的下一个类,发现是 B,于是,跳到了 B 的 init,这时会打印出 enter B,而不是 enter Base。 整个过程还是比较清晰的,关键是要理解 super 的工作方式,而不是想当然地认为 super 调用了父类的方法。 小结 事实上,super 和父类没有实质性的关联。super(cls, inst) 获得的是 cls 在 inst 的 MRO 列表中的下一个类。 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |