面向对象之继承与派生
一、继承
继承是一种创建新类的方式,在python中,新建的类可以继承一个或者多好个父类,新建的类课称为子类或派生类,父类称为基类或超类。
通过类的内置属性__bases__可以查看类继承的所有父类。
#父类 class People: school = 'star' country = 'China' def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex #子类 class Teacher(People): def change_score(self): print(f'{self.name}正在修改分数') class Student(People): def choose_course(self): print(f'{self.name}正在选择课程') stu1 = Student('nick',18,'male') tea1 = Teacher('Pam',19,'male') print(Student.__bases__) #(<class '__main__.People'>,) print(stu1.name,stu1.school,stu1.country,stu1.age,stu1.sex) #nick star China 18 male print(tea1.name,tea1.school,tea1.country,tea1.age,tea1.sex) #Pam star China 19 male
二、继承与抽象
要找出类与类之间的继承关系,需要先抽象,再继承。抽象即总结像是相似之处,总结对象之间的相似之处得到类,总结类与类之间的相似之处就可以得到父类。
继承的作用:
子类可以继承父类的所有属性,因而继承可以用来解决类与类之间代码的重用性问题。减少代码的冗余。
Teacher类中没有定义__init__方法,但是会从父类中找到__init__从而正常实例化。
#子类 class Teacher(People): def change_score(self): print(f'{self.name}正在修改分数')
三、属性查找
对象在查找属性时,先从自己的__dict__中找,未找到,则去子类中找,未找到,再去父类中找。
#查找顺序 class Foo: x = 10 class Bar(Foo): x = 100 pass obj = Bar() obj.x = 1000 #给对象添加属性 print(obj.x) #1000 先从自己的空间找
class Foo: x = 10 class Bar(Foo): x = 100 pass obj = Bar() print(obj.x) #100 去子类找
class Foo: x = 10 class Bar(Foo): pass obj = Bar() print(obj.x) #10 子类没有去父类
四、继承的实现原理
MRO是一个简单的所有基类的线性顺序列表。
在python中子类可以同时继承多个父类,经典类与新式类会又不同的MRO,分别对应属性的两种查找方式: 深度优先 和 广度优先。
class B: def test(self): print('from B.test') class C(A, B): pass c = C() print(C.mro()) #[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>] # 去A找,有的话打印,然后super又执行了test,根据mro中查找打印B类中test。 c.test() #from A.test #from B.test
多继承的情况下造成 “ 砖石继承 ”
# 新式类: class A(object): # def test(self): # print('from A') pass class B(A): # def test(self): # print('from B') pass class C(A): # def test(self): # print('from C') pass class D(B): # def test(self): # print('from D') pass class E(C): # def test(self): # print('from E') pass class F(D, E): # def test(self): # print('from F') pass # F-->D-->B-->E-->C-->A-->object # print(F.mro()) obj = F() obj.test()
五、派生与方法重用
子类可以派生自己新的属性,在查找属性时,若子类的方法名与父类相同,优先查找子类然后是父类。
class Foo: def f1(self): print('from Foo.f1') def f2(self): print('from Foo.f2') self.f1() class Bar(Foo): def f1(self): print('from Bar.f1') def func(self): print('from Bar.func') bar_obj = Bar() bar_obj.f1() #from Bar.f1 bar_obj.func() #from Bar.func bar_obj.f2() #from Foo.f2 from Bar.f1 对象————>Bar————>Foo
子类中的新属性在__init__内的代码和父类代码重复,解决方法:
方法一:
直接调用某一个类的函数
#子类 class Teacher(People): def __init__(self,name,age,sex,sal): People.__init__(self,name,age,sex) #调用的是函数,需要几个参数就要传入几个参数 self.sal = sal def change_score(self): print(f'{self.name}正在修改分数')
方法二:
调用super() 方法会得到一个特殊对象,该对象专门引用父类属性,且严格按照MRO规定顺序查找
#子类 class Teacher(People): def __init__(self,name,age,sex,sal): super().__init__(name,age,sex) #调用绑定方法,自动传入self self.sal = sal def change_score(self): print(f'{self.name}正在修改分数')
注意使用规范:在python2 中 :super( 自己的类名,self )
在python3 中 :super( )
类的分类:
新式类:凡是继承 object 的类或子类都是新式类。
在python3 中,所有的类都默认继承object。
经典类:在python2中,才会有经典类与新式类之分。
在python2中,凡是没有继承 object 的类,都是经典类。
class User(object): pass class User: # (<class 'object'>,) x = 10 pass class Sub(User): pass print(User.__dict__) print(object)