Python3内置函数汇总

目录

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

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

  1. 单继承的类层次结构中,例如:class B(A)、class C(A)。super()函数可用于以访问其父类的方法,而不显式命名父类。从而可以使代码易于维护(显式命名在修改父类名称的时候比较崩溃)。
  2. 多继承的类层次结构中,Python支持当前类同时继承多个类作为其父类。并且按照调用类的MRO(方法解析顺序 - method resolution order)顺序进行向上查找。

super

语法

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),例如:<super: <class 'C'>, <D object>>,一般都是直接调用函数,不需要返回值。

实例

§ 实例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

运行结果分析:

  1. 执行B类plus方法
  2. B类plus方法super()函数调用A类的plus方法,并将B类的self传递给A类。
  3. A类plus方法执行self.n += m(3 + 2 = 5)
  4. 父类执行完毕,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

运行分析:

  1. 执行D类plus方法
  2. D类plus方法super()函数调用B类的plus方法,并将D类的self传递给B类。
  3. B类plus方法super()函数调用C类的plus方法,并将D类的self传递给C类。
  4. C类plus方法super()函数调用A类的plus方法,并将D类的self传递给A类。
  5. A类plus方法执行self.n += m(5 + 2 = 7)
  6. C类plus方法执行self.n += m(7 + 4 = 11)
  7. B类plus方法执行self.n += m(11 + 3 = 14)
  8. 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

如果有任何建议或意见,欢迎交流沟通

作者:张恺阳

本文链接:https://www.zky.name/article/67.html

来源:张恺阳博客