面向对象有3大特性:继承、多态、封装,本章介绍 Python中的 继承 特性~
算法
继承是建立类的一种方式,在 Python中支持多继承,即在建立类的时候能够继承一个或者多个父类。
在继承关系中,被继承的类一般称为父类(或超类,基类),新建的类则称为子类(或派生类)。
继承的优点在于能够有效地重用代码,提升代码的可读性~
继承示例:ide
class Fu_1: # 父类 pass class Fu_2: # 父类 pass class Zi_1(Fu_1): # 单继承 pass class Zi_2(Fu_1, Fu_2): # 多继承 pass
上述示例中,Fu_1 和 Fu_2 没有继承任何类,在 Python3 中,这样就会默认继承object类,而在 Python2 中,默认不会继承 object类,注意区分 ~
可经过 类的内置属性 __bases__ 查看这个类 继承的全部父类函数
print(Zi_1.__bases__) print(Zi_2.__bases__) # 输出结果: (<class '__main__.Fu_1'>,) (<class '__main__.Fu_1'>, <class '__main__.Fu_2'>)
在开发过程当中,若新建的一个类和已建立的另外一个类 属性及方法大体相同,则可让新建的类(子类)继承已建立的类(父类),这样子类会继承父类的全部属性,包括数据属性和函数属性,实现了代码重用,代码变得简洁,能够有效缩短开发周期~code
class Father: def __init__(self, name, age): self.name = name self.age = age def say(self): print('Hello !') class Son(Father): pass p = Son('baby', 19) p.say() # 输出结果: Hello !
在继承过程当中,子类也能够添加或者从新定义这些属性,当父类和子类中有同名的属性时(包括数据属性和函数属性),会先调用子类中的属性(操做的是子类的实例化对象)对象
class Father: city = 'NB' def __init__(self, name, age): self.name = name self.age = age def say(self): print('Hello ! ' + self.city) class Son(Father): city = 'HZ' def say(self): print('你好 ~ ' + self.city) def eat(self): pass p = Son('baby', 19) p.say() # 输出结果: 你好 ~ HZ
在子类的函数中,如果要重用父类中某个函数的功能,能够直接经过 super 来调用父类中的函数,固然也能够经过 类名.func() 来调用,只不过这样与调用普通函数无异。这个常常使用在须要对父类的同名方法进行扩展的场景~blog
class Father: def say(self): print('Hello !') def introduce(self): print('Father') class Son(Father): def say(self): super().say() # Father.say(self) # 经过 类名.func(),输出结果一致 print('你好 ~') p = Son() p.say() # 输出结果 Hello ! 你好 ~
上述示例中,使用 super的时候省略了2个参数:Son(当前类名称,注意不是父类),self(当前对象)继承
super().say() # 等同于 super(Son, self).say()
因为 super 方法中已经默认传递了self参数,因此后面的函数不须要再次传递self~
注意:super关键字只在新式类中有,Python3中全部的类都是新式类...
在子类的函数中使用super方法,不必定仅调用同名的父类函数,也能够调用其余的父类函数~ip
def say(self): super().introduce() print('你好 ~')
以下示例中子类对父类的 init方法 进行了扩展,这是一种较为经常使用的使用方式~ci
class Father: def __init__(self, name, age): self.name = name self.age = age def say(self): print('Hello !') class Son(Father): def __init__(self, name, age, hobby): super().__init__(name, age) self.hobby = hobby def say(self): super().say() print('你好 ~')
super方法不光能够在类的内部使用,也能够在类的外部的使用,在类的外部使用时,super方法不能够省略参数开发
# 外部使用super p = Son() super(Son, p).say() # 输出结果: Hello !
上面已经说过,Python3 中全部的类都是新式类,新建的类没有继承任何类的时候,会默认继承 object 类。
在 Python2中,经典类和新式类并存,新建的类如果没有继承任何类,则这个类为经典类,只有显示地继承了 object 类或其子类,这个类才是新式类~
# Python2中 class C1: # 经典类 pass class C2(C1): # 经典类 pass class C3(object): # 新式类 pass # Python3中 class N1: # 新式类 pass class N2(N1): # 新式类 pass class N3(object): # 新式类 pass
在Python中支持多继承,新式类和经典类的继承顺序有所差别,如下以钻石继承为例给出示例:
class A(object): def fun(self): print('from A') class B(A): def fun(self): print('from B') class C(A): def fun(self): print('from C') class D(B): def fun(self): print('from D') class E(C): def fun(self): print('from E') class F(D, E): # def fun(self): # print('from F') pass
上述多个类的继承关系以下图所示:
当前环境为Python3,即新式类,可经过内置的__mro__方法查看继承顺序:
f1 = F() # f1.fun() print(F.__mro__) # 输出结果: (<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
即新式类的继承顺序:F->D->B->E->C->A,广度优先。
那什么是继承顺序呢?就是寻找父类的顺序,例如这里调用 f1对象(F类的实例化对象)的 fun方法,若F类中没有这个方法,就去D类中寻找,若D类中也没有,就去B类中寻找,而后是E类,C类,A类,即按照 MRO列表上 从左到右查找基类~
若这里的类是经典类,则继承顺序为:F->D->B->A->E->C ,深度优先~
广度优先 与 深度优先 的区别在于,广度优先算法在查找基类的时候,若以后能找到的,则以后再进行查找,若以后找不到的,如今就去查找。例如,经过E,C也能再次找到A,则先不找A,可是经过E,C不能再次找到B,因此查找D以后就找B,查找B以后不会去查找A,而是在C以后再去查找A~
再看以下示例,因为B和C只能经过一条途径找到,因此新式类和经典类的继承顺序一致:
新式类继承顺序:F->D->B->E->C
经典类继承顺序:F->D->B->E->C
如果在钻石继承中用到了 super 关键字,super 会去调用父类的对应方法,可是 super 的本质并非直接找父类,而是根据调用者的节点位置的广度优先顺序来查找的。即 按照广度优先的继承顺序找到上一个类~
Tip:super 关键字只有在新式类中有,因此确定是按照广度优先的继承顺序来进行查找的~
class A: def func(self): print('A') class B(A): def func(self): super().func() print('B') class C(A): def func(self): super().func() print('C') class D(B, C): def func(self): super().func() print('D') d = D() d.func() # 输出结果: A C B D
上述多个类的继承关系以下图所示:当前使用的是新式类,继承顺序为:D->B->C->A(经典类的继承顺序为:F->B->A->C) 其中super的调用过程以下:D类的 func() 方法中的 super().func() 会调用 B类的 func() 方法,B类的 func() 方法中的super().func() 会调用 C类的 func() 方法,C类的 func() 方法中的 super().func() 会调用 A类的 func() 方法~ 固然在单继承中不会有这样的问题~ .................^_^