pythonsuper函数_Python3 super() 函数详解 隐式访问父类(超类)的被重载方法

Python3 super() 函数详解 隐式访问父类(超类)的被重载方法

super()函数的主要作用是调用父类(超类)以及多层父类的方法,这对于访问已在类中重写的继承方法很有用。super()函数比较常用的情况有2种。

单继承的类层次结构中,例如:class B(A)、class C(A)。super()函数可用于以访问其父类的方法,而不显式命名父类。从而可以使代码易于维护(显式命名在修改父类名称的时候比较崩溃)。

多继承的类层次结构中,Python支持当前类同时继承多个类作为其父类。并且按照调用类的MRO(方法解析顺序 - method resolution order)顺序进行向上查找。

a175c8205cfd70513bb8ac8809fbbe65.jpg

语法

super([type[, object-or-type]])

参数

type[可选] - 类,如填写则一般为当前类名。

object-or-type[可选] - 对象或者类,如果该参数为对象(例如:super(type, obj))则isinstance(obj, type)必须等于True(对象obj必须属于类type,直接、间接或虚拟均可),如果该参数为类(例如:super(type, type2))则issubclass(type2, type)必须等于True(类type2必须是类type的子类,直接、间接或虚拟均可),如填写一般为self。

在python3种这2个参数均可以不填,默认type为当前类,object-or-type为当前类对象。

默认MRO为当前类 + 父类(包括兄弟类) + object,调用的顺序是按照从左到右依次执行的。需要注意的是,它会跳过了当前类(type)本身。

返回值

返回当前super()函数的类和对象信息(type和object-or-type),例如:, >,一般都是直接调用函数,不需要返回值。

实例

§ 实例1 - 单继承

代码如下:

#声明A类

class A:

def __init__(self):

self.n = 2

def plus(self, m):

print('当前对象是 {} 来自 A类的plus方法'.format(self))

#这里的self.n并不是A类的self.n,而是B类的self.n。因为super()第2个参数是调用者的self。

self.n += m

#声明B类,继承A类

class B(A):

def __init__(self):

self.n = 3

def plus(self, m):

print('当前对象是 {} 来自 B类的plus方法'.format(self))

#调用A类的plus方法

super().plus(m)

self.n += 3

#实例化B类

b = B()

#调用B类的plus方法

b.plus(2)

#打印B类的n属性

print(b.n)

运行结果:

当前对象是 <__main__.B object at 0x00000000027EDCF8> 来自 B类的plus方法

当前对象是 <__main__.B object at 0x00000000027EDCF8> 来自 A类的plus方法

8

运行结果分析:

执行B类plus方法

B类plus方法super()函数调用A类的plus方法,并将B类的self传递给A类。

A类plus方法执行self.n += m(3 + 2 = 5)

父类执行完毕,B类的plus方法中的super()函数执行完毕,运行self.n += m (5 + 3 = 8)。

§ 实例2 - 优势 - 隐式调用重载方法

super()有一个非常好的优势,就是可以隐式的调用父类的当前类重载掉的方法。

代码如下:

class A:

def __init__(self):

self.n = 2

def plus(self, m):

print('当前对象是 {} 来自 A类的plus方法'.format(self))

self.n += m

#声明B类,继承A类

class B(A):

def __init__(self):

self.n = 3

def plus(self, m):

print('当前对象是 {} 来自 B类的plus方法'.format(self))

super().plus(m)

#self.plus(m) 死循环。下面详解。

#A.plus(self,m) 与super().plus(m) 效果一致,但有隐患,下面详解。

self.n += 3

b = B()

b.plus(2)

print(b.n)

正确的运行结果与实例1相同。

我们先来讲一下死循环,由于父类(A)的plus()方法被B类重载了,就导致self.plus()在查找时找到的都是B类plus()方法,那么就不断的进入B类的plus,然后再不断的运行self.plus。最终导致Python报错:maximum recursion depth exceeded while getting the str of an object(超出最大递归深度),还好Python有个限制机制会帮我们拦截了这种错误的行为,否则要死机咯。

再来说一下显式调用父类被重载方法,也就是代码中的A.plus(self,m)。虽然A类的plus被它的子类(B)给重载了,但并不是不存在了,只要我们指明调用类还是可以使用的,并且给它一个self对象,运行环境也是B类的。结果和super()函数是一致的,看上去并没什么问题,但它存在一定的维护隐患,在例子中由于代码很少,所以修改起来比较容易,万一有上万行的代码,还带有模块间的引用,这时A类的名称变了,错误就比较多了,要改的地方也非常多,得一点点改。继承父类(class B(A))时是没办法,只能写清楚父类的名称(PS:也有办法集中管理),但调用父类被重载方法是可以隐式的,所以请尽量减少类名的指定,这不是必须的,但这是一个好习惯。

§ 实例3 - 多继承

代码如下:

#声明A类

class A:

def __init__(self):

self.n = 2

def plus(self, m):

print('当前对象是 {} 来自 A类的plus方法'.format(self))

self.n += m

#声明B类,继承A类

class B(A):

def __init__(self):

self.n = 3

def plus(self, m):

print('当前对象是 {} 来自 B类的plus方法'.format(self))

#调用C类的plus方法

super().plus(m)

self.n += 3

#声明C类,继承A类。

class C(A):

def __init__(self):

self.n = 4

def plus(self, m):

print('当前对象是 {} 来自 C类的plus方法'.format(self))

#调用A类的plus方法

super().plus(m)

self.n += 4

#声明D类,继承B和C类,这样D类就有2个父类咯。B和C就成为了兄弟类。

#D类的MRO是[D,B,C,A,Object]

class D(B, C):

def __init__(self):

self.n = 5

def plus(self, m):

print('当前对象是 {} 来自 D类的plus方法'.format(self))

#调用B类的plus方法

#一定要记住一点,在这个例子里,所有类中的super()函数都没有指定参数,所以从D类一直super()到A类,所有的self都是D类的self。

super().plus(m)

self.n += 5

#实例化D类

d = D()

#调用D类的plus()方法

d.plus(2)

#输出D类的n属性

print(d.n)

运行结果:

当前对象是 <__main__.D object at 0x000000000222DCF8> 来自 D类的plus方法

当前对象是 <__main__.D object at 0x000000000222DCF8> 来自 B类的plus方法

当前对象是 <__main__.D object at 0x000000000222DCF8> 来自 C类的plus方法

当前对象是 <__main__.D object at 0x000000000222DCF8> 来自 A类的plus方法

19

运行分析:

执行D类plus方法

D类plus方法super()函数调用B类的plus方法,并将D类的self传递给B类。

B类plus方法super()函数调用C类的plus方法,并将D类的self传递给C类。

C类plus方法super()函数调用A类的plus方法,并将D类的self传递给A类。

A类plus方法执行self.n += m(5 + 2 = 7)

C类plus方法执行self.n += m(7 + 4 = 11)

B类plus方法执行self.n += m(11 + 3 = 14)

D类plus方法执行self.n += m(14 + 5 = 19)

§ 实例4 - 设置MRO并指定起始类

基础代码就不写了,基本和实例2一样。有A,B,C,D四个类,只有D类不同,所以下面只重写了D类。

代码如下:

class D(B, C):

def __init__(self):

self.n = 5

def plus(self, m):

print('当前对象是 {} 来自 D类的plus方法'.format(self))

super(C,self).plus(m)

#super(C,D).plus(self,m) 和上一句效果相同。

self.n += 5

#实例化D类

d = D()

#调用D类的plus()方法

d.plus(2)

#输出D类的n属性

print(d.n)

运行结果:

当前对象是 <__main__.D object at 0x000000000280DCF8> 来自 D类的plus方法

当前对象是 <__main__.D object at 0x000000000280DCF8> 来自 A类的plus方法

12

运行分析:

由于指定了从D的MRO列表中C类开始,按规则会跳过本身类,也就是跳过C类,所以真是运行的只有A和D类。

结果也就是5+2+5=12咯。

§ 实例5 - 父类非重载方法的调用

前面的例子我们都是在调用与父类一样名称的方法(被子类重载的),plus()方法。但super()函数并不是一定要调用父类的同名(被重载)的方法,也可以调用其它的,不过这并不常用,因为如果父类方法不是被重载,那么就被继承到了子类,也就可以直接使用self.x()调用。下面做个简单的演示。

代码如下:

#声明A类

class A:

def __init__(self):

self.n = 2

def run(self, m):

print('当前对象是 {} 来自 A类的plus方法'.format(self))

self.n += m

#声明B类,继承A类

class B(A):

def __init__(self):

self.n = 3

def plus(self, m):

print('当前对象是 {} 来自 B类的plus方法'.format(self))

super().run(m)

#self.run(m) 结果是一样的

self.n += 3

b = B()

b.plus(2)

print(b.n)

运行结果:

当前对象是 <__main__.B object at 0x00000000021FD908> 来自 B类的plus方法

当前对象是 <__main__.B object at 0x00000000021FD908> 来自 A类的plus方法

8

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值