面向对象三大特性之继承
程序员文章站
2022-10-04 12:58:57
面向对象三大特性之继承 一、继承初体验 什么是继承 继承的作用 如何实现继承法? 小例子 二、如何寻找继承关系 如何寻找继承关系 传统写法,代码冗余 用父类去书写,解决代码冗余 三、在继承属性下对象属性的查找属性 注意 查找顺序演示 四、派生 派生 五、子类继承父类并重用父类的属性的方法 代码 六、 ......
面向对象三大特性之继承
一、继承初体验
- 什么是继承
继承是一种新建类的方式,新建的类称之为子类或派生类,继承的父类称之为基类或超类。 - 在python中,一个子类可以继承多个父类。(面试可能会问) - 在其它语言中,一个子类只能继承一个父类。
- 继承的作用
减少代码冗余
- 如何实现继承法?
1) 先确认谁是子类,谁是父类。 2) 在定义类子类时, 子类名(父类名)。
- 小例子
#父类 class father1: x = 1 pass class father2: pass class father3: pass #子类 class sub(father1,father2,father3): pass #子类.__bases__查看父类 print(sub.__bases__) #(<class '__main__.father1'>, <class '__main__.father2'>, <class '__main__.father3'>) print(sub.x) #1
二、如何寻找继承关系
- 如何寻找继承关系
- 确认谁是子类 - 小小明对象 ---> 人子类 ---> 动物父类 - 猪坚强对象 ---> 猪子类 ---> 动物父类 - 哈士奇对象 ---> 狗子类 ---> 动物父类 - 人、猪、狗 都是子类 - 确认谁是父类 - 动物类是父类 - 得先抽象,再继承 - 抽取对象之间相似的部分,总结出类 - 抽取类之间相似的部分,总结出父类。
- 传统写法,代码冗余
#老师类 class teacher: school = 'oldboy' country = 'china' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex #老师改分 def change_limit(self): print(f'老师{self.name}开始批改分数...') #学生类 class student: school = 'oldboy' country = 'china' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex #学生选课 def choose_course(self, name, age, sex): self.name = name self.age = age self.sex = sex tea1 = teacher('tank', 28, 'male') print(tea1.name, tea1.age, tea1.sex) #tank 28 male stu1 = student('yafeng', 18, 'male') print(stu1.name, stu1.age, stu1.sex) #yafeng 18 male '''这样写虽然也可以拿到我想要的信息,但是代码很长,且有很多代码重复,下面的父类就是解决这种情况'''
- 用父类去书写,解决代码冗余
#父类 class oldpeople: school = 'oldboy' country = 'china' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex #子类 #学生类 class oldstudent(oldpeople): def chose_course(self): print(f'学生{self.name}正在选课!') #老师类 class oldteacher(oldpeople): def change_score(self): print(f'老师{self.name}正在修改分数') obj1 = oldstudent('yafeng', 18, 'male') print(obj1.name, obj1.age, obj1.sex) #yafeng 18 male obj2 = oldteacher('tank', 25, 'male') print(obj2.name, obj2.age, obj2.sex) #tank 25 male
三、在继承属性下对象属性的查找属性
- 注意
''' 注意: 程序的执行顺序是由上到下,父类必须定义在子类的上方。 - 在继承背景下,对象属性的查找顺序: 1.先从对象自己的名称空间中查找 2.对象中没有,从子类的名称空间中查找。 3.子类中没有, 从父类的名称空间中查找,若父类没有,则会报错! '''
- 查找顺序演示
#父类 class father: x = 10 pass #子类 class sub(father): x = 100 pass obj1 = sub() # print(obj1.x) #100 先从对象自己的名称空间中查找 # print(obj1.x) #10,此时把x=100注释掉,对象中没有,从子类的名称空间中查找。 # print(obj1.x) #将父类的x=10也注释掉attributeerror: 'sub' object has no attribute 'x' #注意: obj1.x = 1000#这是给对象添加属性的操作,并不是修改子类的属性 print('子类的名称空间', sub.__dict__) #子类的名称空间 {'__module__': '__main__', 'x': 100, '__doc__': none} print('对象的名称空间', obj1.__dict__) #对象的名称空间 {'x': 1000} print('父类的名称空间', father.__dict__) #父类的名称空间 {'__module__': '__main__', 'x': 10, '__dict__': <attribute '__dict__' of 'father' objects>, # '__weakref__': <attribute '__weakref__' of 'father' objects>, '__doc__': none}
四、派生
- 派生
''' 派生: 指的是子类继承父类的属性与方法,并且派生出自己独有的属性与方法。 若子类中的方法名与父类的相同,优先用子类的。 ''' # #父类 # class foo: # def f1(self): # print('from foo.f1...') # # def f2(self): # print('from foo.f2...') # # # #子类 # class goo(foo): # # #重写(其实python中根本没有重写一说,姑且就称之为重写) # def f1(self): # print('from goo.f1...') # # # def func(self): # print('from goo.func...') # # obj = goo() # print(obj.f1()) #from goo.f1... # print(obj.f2()) #from foo.f2... # print(obj.func()) #from goo.func... #父类 class foo: def f1(self): print('from foo.f1...') def f2(self): print('from foo.f2...') self.f1() #子类 class goo(foo): #重写(其实python中根本没有重写一说,姑且就称之为重写) def f1(self): print('from goo.f1...') def func(self): print('from goo.func...') obj = goo() obj.f2() #答案是多少? #'from foo.f2...' 'from goo.f1...'
五、子类继承父类并重用父类的属性的方法
- 代码
''' - 子类继承父类,派生出自己的属性与方法,并且重用父类的属性与方法。 ''' '''需求:此时要给子类老师类添加新的属性薪水,给子类中的学生类添加女票''' #方法一:直接在子类中添加ps(那我还要你父类干嘛) #父类 # class oldpeople: # school = 'oldboy' # country = 'china' # # def __init__(self, name, age, sex): # self.name = name # self.age = age # self.sex = sex # # # #子类 # #学生类 # class oldstudent(oldpeople): # # def __init__(self, name, age, sex, girl_friend): # self.name = name # self.age = age # self.sex = sex # self.girl_friend = girl_friend # # def chose_course(self): # print(f'学生{self.name}正在选课!') # # # #老师类 # class oldteacher(oldpeople): # # def __init__(self, name, age, sex, sal): # self.name = name # self.age = age # self.sex = sex # self.sal = sal # # def change_score(self): # print(f'老师{self.name}正在修改分数') # # # # obj1 = oldstudent('yafeng', 18, 'male', '热巴') # print(obj1.name, obj1.age, obj1.sex, obj1.girl_friend) #yafeng 18 male 热巴 # # # obj2 = oldteacher('tank', 25, 'male', 15000) # print(obj2.name, obj2.age, obj2.sex, obj2.sal) #tank 25 male 15000 ''' 解决需求: 子类重用父类的属性,并派生出新的属性。 两种方式: 1.直接引用父类的__init__为其传参,并添加子类的属性。 2.通过super来指向父类的属性。 - super()是一个特殊的类,调用super得到一个对象,该对象指向父类的名称空间。 注意: 使用哪一种都可以,但不能两种方式混合使用。 ''' #方式一:直接引用父类的__init__为其传参,并添加子类的属性。 # #父类 # class oldpeople: # school = 'oldboy' # country = 'china' # # def __init__(self, name, age, sex): # self.name = name # self.age = age # self.sex = sex # # #子类 # #学生类 # class oldstudent(oldpeople): # # def __init__(self, name, age, sex, girl_friend): # # # 类调用类内部的__init__,只是一个普通函数 # oldpeople.__init__(self, name, age, sex) # self.girl_friend = girl_friend # # def chose_course(self): # print(f'学生{self.name}正在选课!') # # # #老师类 # class oldteacher(oldpeople): # # def __init__(self, name, age, sex, sal): # oldpeople.__init__(self, name, age, sex) # self.sal= sal # # def change_score(self): # print(f'老师{self.name}正在修改分数') # # obj1 = oldstudent('yafeng', 18, 'male', '热巴') # print(obj1.name, obj1.age, obj1.sex, obj1.girl_friend) #yafeng 18 male 热巴 # # # obj2 = oldteacher('tank', 25, 'male', 15000) # print(obj2.name, obj2.age, obj2.sex, obj2.sal) #tank 25 male 15000 #方式二:2.通过super来指向父类的属性。 #- super()是一个特殊的类,调用super得到一个对象,该对象指向父类的名称空间。 # 父类 class oldpeople: school = 'oldboy' country = 'china' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex #子类 #学生类 class oldstudent(oldpeople): def __init__(self, name, age, sex, girl_friend): super().__init__(name, age, sex)#注意此时使用super()方法,不用传self self.girl_friend = girl_friend def chose_course(self): print(f'学生{self.name}正在选课!') #老师类 class oldteacher(oldpeople): def __init__(self, name, age, sex, sal): super().__init__(name, age, sex) self.sal = sal def change_score(self): print(f'老师{self.name}正在修改分数') obj1 = oldstudent('yafeng', 18, 'male', '热巴') print(obj1.name, obj1.age, obj1.sex, obj1.girl_friend) #yafeng 18 male 热巴 obj2 = oldteacher('tank', 25, 'male', 15000) print(obj2.name, obj2.age, obj2.sex, obj2.sal) #tank 25 male 15000
六、经典类与新式类
- 了解
#!/usr/bin/u/ubv/a python #_*_ coding:utf8 _*_ ''' 经典类与新式类: (了解) - 工作中遇不到 - 面试有可能会问 - 新式类: 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__)
七、super严格遵循mro继承顺序原则
- 了解
''' 调用mro返回的是一个继承序列: (了解知识点) super的继承顺序严格遵循mro继承序列。 ''' class father1: x = 10 pass class father2: x = 100 pass class sub(father1, father2): pass print(sub.mro()) #[<class '__main__.sub'>, <class '__main__.father1'>, <class '__main__.father2'>, <class 'object'>] obj = sub() print(obj.x) #10 ''' 在python3中提供了一个查找新式类查找顺序的内置方法. mro(): 会把当前类的继承关系列出来。 ''' # 注意: super()会严格按照mro列表的顺序往后查找 class a: 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的继承顺序 print(c.mro()) # # 去a找,有的话打印,然后super又执行了test,根据mro中查找打印b类中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 # print(f.mro()) obj = f() obj.test()