一、继承介绍
1、什么是继承
继承是一种新建类的方式,新建的类称之为子类,
被继承的类称之为父类、基类、超类
python支持多继承,用逗号分隔开多个继承的类
查看继承:派生类名.__bases__ #__base__只查看从左到右继承的第一个父类,__bases__则是查看所有继承的父类
2、为何要继承
子类会遗传父类的属性,所以继承是用来解决类与类之间代码冗余问题
PS:类是为了解决对象与对象之间代码冗余问题
3、如何实现继承
ps:单继承能更好的反应什么是什么的关系
# 继承案列: class OldboyPeople: school = "oldboy" class Student(OldboyPeople): def __init__(self,name,age,gender,stud_id,course): self.name = name self.age = age self.gender = gender self.stu_id = stud_id self.course = course def choose(self): print('%s 正在选课' %self.name) class Teacher(OldboyPeople): def __init__(self,name,age,gender,salary,level): self.name = name self.age = age self.gender = gender self.salary = salary self.level = level def score(self,stu,num): stu.num = num stu1=Student("艾利克斯",73,'male',1001,"python全栈开放") tea1=Teacher("egon",18,'male',2000,10) print(stu1.school)
二、在子类派生的新方法中重用父类的功能
方式一:指名道姓地调用某一个类的函数
特点:不依赖于继承关系
class OldboyPeople: school = "oldboy" # 空对象,"艾利克斯",73,'male' def __init__(self,name,age,gender): self.name = name self.age = age self.gender = gender def f1(self): print('1111111') class Student(OldboyPeople): # 空对象,"艾利克斯",73,'male',1001,"python全栈开放" def __init__(self,name,age,gender,stu_id,course): OldboyPeople.__init__(self,name,age,gender) # OldboyPeople.__init__(空对象,"艾利克斯",73,'male') self.stu_id = stu_id self.course = course def choose(self): print('%s 正在选课' %self.name) def f1(self): OldboyPeople.f1(self) print("22222") class Teacher(OldboyPeople): def score(self,stu,num): stu.num = num stu1=Student("艾利克斯",73,'male',1001,"python全栈开放") # tea1=Teacher("egon",18,'male',2000,10) stu1.f1()
方式二:调用super(自己的类名,self)会返回一个特殊的对象,super(自己的类名,self).属性,会参照属性查找发起的那个类的mro列表去它父类中查找属性
特点:严格依赖于继承关系
#1 class OldboyPeople: school = "oldboy" # 空对象,"艾利克斯",73,'male' def __init__(self,name,age,gender): self.name = name self.age = age self.gender = gender def f1(self): print('1111111') class Student(OldboyPeople): def __init__(self,name,age,gender,stu_id,course): # OldboyPeople.__init__(self,name,age,gender) # OldboyPeople.__init__(空对象,"艾利克斯",73,'male') super(Student,self).__init__(name,age,gender) self.stu_id = stu_id self.course = course def choose(self): print('%s 正在选课' %self.name) def f1(self): # OldboyPeople.f1(self) # super().f1() print("22222") # print(Student.mro()) stu1=Student("艾利克斯",73,'male',1001,"python全栈开放") # print(stu1.__dict__) stu1.f1() #2: class A: def test(self): super().test() class B: def test(self): print('from B') class C(A,B): pass c=C() print(C.mro()) c.test() obj=A() obj.test() #3: class A: def test(self): print('A---->test') super().aaa() class B: def test(self): print('B---->test') def aaa(self): print('B---->aaa') class C(A,B): def aaa(self): print('C----->aaa') c=C() print(C.mro()) c.test()
三、属性查找
# 例1:有了继承关系,对象在查找属性时,先从对象自己的__dict__中找,如果没有则去子类中找,然后再去父类中找 class Foo: def f2(self): print("Foo.f2") def f1(self): print('Foo.f1') self.f2() # obj.f2() class Bar(Foo): def f2(self): print("Bar.f2") obj = Bar() obj.f1()
# 例2:父类如果不想让子类覆盖自己的方法,可以在方法名前加前缀__ class Foo: def __f2(self): # _Foo__f2 print("Foo.f2") def f1(self): print('Foo.f1') self.__f2() # obj._Foo__f2() class Bar(Foo):#父类为Foo def __f2(self): # _Bar__f2 print("Bar.f2") obj = Bar() obj.f1()
四、继承的实现原理
# coding:utf-8 (在python2中要写文件头才能保证不乱码,python3底层会有优化机制不用写)
1、 补充知识:
新式类:但凡是继承了object类的子类,以该子类子子孙孙类都称之为新式类
经典类:没有继承了object类的子类,以该子类子子孙孙类都称之为经典类
python3中无论是否继承object,都默认继承object,即python3中全都是新式类。
python2中才有经典类
python2中没有显式的继承object的类,以及该类的子类,都是经典类;
python2中显式地声明继承object的类,以及该类的子类,都是新式类。
pyhon2中新式类要主动继承,手动加上object,加上文件头
class Foo(object):
pass
print(Foo.__bases__) #通过类的内置属性__bases__可以查看类继承的所有父类
2、 继承的实现原理
继承(先抽象再继承):是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
对于你定义的每一个类,Python都会计算出一个方法解析顺序(MRO)列表,该MRO列表就是一个简单的所有基类的线性顺序列表
python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止
查找顺序:(1)子类会先于父类被检查
(2)多个父类会根据它们在列表中的顺序被检查
(3)如果对下一个类存在两个合法的选择,选择第一个父类
ps:
1.由对象发起的属性查找,会从对象自身的属性里检索,没有则会按照对象的类.mro()规定的顺序依次找下去, 2.由类发起的属性查找,会按照当前类.mro()规定的顺序依次找下去
2.1 菱形问题(有时也称死亡钻石):一个子类继承的多条件分支最终汇聚到一个非object类,在菱形继承下
新式类与经典类关于属性查找的方式不同
新式类:广度优先
经典类:深度优先
# 例1:非菱形继承,经典类与新式类的属性查找顺序都一样 class E: def test(self): print('from E') pass class F: def test(self): print('from F') class B(E): def test(self): print('from B') pass class C(F): def test(self): print('from C') class D: def test(self): print('from D') class A(B, C, D): def test(self): print('from A') pass obj=A() obj.test()
# 例2:菱形继承 class G(object): # 在python2中,未继承object的类及其子类,都是经典类 def test(self): print('from G') pass class E(G): def test(self): print('from E') pass class F(G): def test(self): print('from F') pass class B(E): def test(self): print('from B') pass class C(F): def test(self): print('from C') pass class D(G): def test(self): print('from D') pass class A(B,C,D): def test(self): print('from A') pass obj=A() obj.test() print(A.mro())
五、mixins机制
mixins机制 遵循'is-a'关系
#不合理 class Vehicle: # 交通工具 def fly(self): ''' 飞行功能相应的代码 ''' print("I am flying") class CivilAircraft(Vehicle): # 民航飞机 pass class Helicopter(Vehicle): # 直升飞机 pass class Car(Vehicle): # 汽车并不会飞,但按照上述继承关系,汽车也能飞了 pass
#Mixins机制 class Vehicle: # 交通工具 pass class FlyableMixin: def fly(self): ''' 飞行功能相应的代码 ''' print("I am flying") class CivilAircraft(FlyableMixin, Vehicle): # 民航飞机 pass class Helicopter(FlyableMixin, Vehicle): # 直升飞机 pass class Car(Vehicle): # 汽车 pass # ps: 采用某种规范(如命名规范)来解决具体的问题是python惯用的套路
使用Mixin类实现多重继承要非常小心
- 首先它必须表示某一种功能,而不是某个物品,python 对于mixin类的命名方式一般以 Mixin, able, ible 为后缀
- 其次它必须责任单一,如果有多个功能,那就写多个Mixin类,一个类可以继承多个Mixin,为了保证遵循继承的“is-a”原则,只能继承一个标识其归属含义的父类
- 然后,它不依赖于子类的实现
- 最后,子类即便没有继承这个Mixin类,也照样可以工作,就是缺少了某个功能。(比如飞机照样可以载客,就是不能飞了)
Mixins是从多个类中重用代码的好方法,但是需要付出相应的代价,我们定义的Minx类越多,子类的代码可读性就会越差,并且更恶心的是,在继承的层级变多时,代码阅读者在定位某一个方法到底在何处调用时会晕头转向