面向对象三大特性之继承
一、继承初体验
继承是一种新建类的方式,新建的类称之为子类或派生类,继承的父类称之为基类或超类。
- 在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()