继承与派生
1、什么是继承"
继承是一种新建类的方式,新建的类称之为子类或派生类,继承的父类称之为基类或超类
-在python中一个子类可以继承多个父类 ******
-在其他语言中,一个子类只能继承一个父类
2、继承的作用
减少代码的冗余
3、如何实现继承?
1)先确认谁是子类,谁是父类
2)在定义子类时,子类名(父类名)
#父类 class Father1: x = 1 pass class Father2: pass class Father3: pass #子类 class Sub(Father1, Father2, Father3): pass #子类.__bases__查看父类 print(Sub.__base__) #<class '__main__.Father1'> print(Sub.x) #1
-如何寻找到继承的关系:
-确认谁是子类:
hcy对象----->人子类------>动物父类
猪坚强对象----->猪子类-------->动物父类
哈士奇对象------->狗子类------->动物父类
-确认谁是父类
-动物类是父类
-抽取对象之间相似的部分,总结出来
-抽取类之间相似的部分,总结出父类
#oldboy人类 class OldboyPeople: school = 'oldbay' country = 'china' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex #老师类 class OldboyTeacher(OldboyPeople): #老师修改分数 def change_score(self): print(f'老师{self.name}正在修改分数。。。。') #学生类 class OldboyStudent(OldboyPeople): #学生选课系统 def choose_course(self): print(f'学生『{self.name}正在选择课程。。。') stu1 = OldboyStudent('YJG', 50, 'female') print(stu1.school, stu1.name, stu1.age, stu1.sex) tea1 = OldboyTeacher('大脸', 75, 'female') print(tea1.school, tea1.name, tea1.age, tea1.sex)
注意:中程序的执行顺序是由上到下,父类必须定义在子类的上方
- 在继承背景下,对象属性的查找顺序:
1.先从对象自己的名称空间中查找
2.对象中没有,从子类的名称空间中查找
3.子类中没有,从父类的名称空间中查找,若父类没有,则会报错!
#父类 class Goo: x =10 pass #子类 class Foo(Goo): x =100 pass foo_obj= Foo() #1) # foo_obj.x = 1000 print(foo_obj.x) #100 print('对象的名称空间:', foo_obj.__dict__) # {} print('子类的名称空间:', Foo.__dict__) #{'__module__': '__main__', 'x': 100, '__doc__': None} print('父类的名称空间:', Goo.__dict__) #{'__module__': '__main__', 'x': 10, '__dict__': <attribute '__dict__' of 'Goo' print('对象的名称空间:', foo_obj.__dict__) #{} #2) foo_obj.x = 1000 print(foo_obj.x) #1000 对象添加属性的操作,并不是修改了子类的属性 print('对象的名称空间',foo_obj.__dict__) #{'x': 1000} print('子类的名称空间',Foo.__dict__ ) #{'__module__': '__main__', 'x': 100, '__doc__': None} print('父类的名称空间', Goo.__dict__) #{'__module__': '__main__', 'x': 10, '__dict__': <attribute '__dict__'... print('对象的名称空间', foo_obj.__dict__) #{'x': 1000}
派生:
指的是子类继承父类的属性方法,并且派生出自己独有的属性与方法
若子类中的方法名与父类的相同,优先用子类的
#父类 class Foo: def f1(self): print('from Foo.f1...') def f2(self): #self ---->bar_obj print('from Foo.f2...') #bar_obj.f1()---->对像自己找---->Bar ----->Foo 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_obj = Bar() bar_obj.f2() ''' 结果1: from Foo.f2... from Bar.f1... '''
派生与继承:
子类继承父类,派生出自己的属性与方法,并且重用父类的属性与方法。
解决问题:子类重用父类的属性,并派生出新的属性
两种方式:
1.直接引用父类的__init__为其传参,并添加子类的属性
2.通过super来指向父类的属性
-super()是一个特殊的类,调用super得到一个对象,该对象指向父类的名称空间
注意:使用哪一种都可以,但不能两种方式混合使用
class People: school = '清华大学' def __init__(self, name, sex, age): self.name = name self.sex = sex self.age = age class Teacher(People): def __init__(self, name, sex, age, title): #派生 self.name = name self.sex = sex self.age = age self.title = title def teach(self): print('%s is teaching'%self.name) obj = Teacher('lili','female', 28, '高级教师') #只会找自己类中的__init__ print(obj.name, obj.sex, obj.age, obj.title) #lili female 28 高级教师
这里Teacher中的__init__内的前三行又是在写重复代码,若想在子类派生出的方法重用父类的功能,可以用两种方式:
# #方式一‘:指名道姓’地调用某一个类的函数 # class Teacher(People): # def __init__(self, name, sex, age, title): # People.__init__(self, name, age, sex) # self.title = title # def teach(self): # print('%s is teaching' %self.name) # obj = Teacher('lili','female', 28, '高级教师') #只会找自己类中的__init__ # # print(obj.teach()) #lili is teaching # #None # #方法二:super() ''' 调用super()会得到一个特殊的对象,该对象专门用来引用父类的属性,且严格按照 MRO规定的顺序向后查找 ''' class Teacher(People): def __init__(self, name, sex, age, title): super().__init__(name, age, sex) #调用的是绑定方法,自动传入self self.title = title def teach(self): print('%s is teaching' %self.name) obj = Teacher('lili','female', 28, '高级教师') #只会找自己类中的__init__ print(obj.teach()) #lili is teaching #None
注意:在python2中super 的使用需要完整地写成super(自己的类名,self),而在python3中可以简写为super()。
-新式类:
1.凡是继承object的类或子孙类都是新式类
2.在python3中所有的类都默认继承object
-经典类:
1.在python2中才会有经典类与新式类之分
2.在python2中,凡是没有继承object的类,都是经典类
class User(object): pass class User: x =10 pass class Sub(User): pass print(User.__dict__) # {'__module__': '__main__', 'x': 10, '__dict__': # <attribute '__dict__' of 'User' objects>, print(object) #<class 'object'>
调用mro 返回的是一个继承序列
super 的继承顺序严格遵循mro继承序列
class Father1: x =10 pass class Father2: x = 20 pass #多继承的情况下,从左到右 class Sub(Father1,Father2): def __init__(self): #注意__int__不是__init__ print(super().__delattr__) print(Sub.mro()) # [<class '__main__.Sub'>, <class '__main__.Father1'>, <class '__main__.Father2'>, <class 'object'>] obj = Sub() print(object) #<class 'object'>
在python3中提供了一种查找新式类查找顺序的内置方法
mro():会把当前类的继承关系列出来
注:supper()会严格按照mro列表的顺序往后查找
class A: #默认继承object def test(self): print('from A.test') super().test() class B: def test(self): print('from B.test') class C(A, B): pass c = C() #检查super的继承顺序 #mro(): 会把当前类的继承关系列出来。 print(C.mro()) #[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>] c.test() #from A.test #from B.test
砖石继承:
多继承情况下造成’“砖石继承”
mro 的查找顺序:
-新式类:
-广度优先
-经典类:
-深度优先
#新式类 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,注意:当class C(A)中A不存在时,则查找顺序为F-D-B-A-E-C->object print(F.mro()) obj = F() obj.test()
如果是经典类,查找顺序为:
F-D-C-A-E-C