继承
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.__bases__) print(Sub.x)
寻找继承关系
- 如何寻找继承关系:
- 确认谁是子类
- 胡晨阳对象 ---> 人子类 ---> 动物父类
- 猪坚强对象 ---> 猪子类 ---> 动物父类
- 哈士奇对象 ---> 狗子类 ---> 动物父类
- 人、猪、狗 都是子类
- 确认谁是父类
- 动物类是父类
- 得先抽象,再继承
- 抽取对象之间相似的部分,总结出类
- 抽取类之间相似的部分,总结出父类。
继承可以解决代码冗余问题
# 老男孩人类 class OldboyPeople: school = 'oldboy' country = 'China' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex # 老师类 class OldboyTeacher(OldboyPeople): # school = 'oldboy' # country = 'China' # def __init__(self, name, age, sex): # self.name = name # self.age = age # self.sex = sex # 老师修改分数 def change_score(self): print(f'老师 {self.name} 正在修改分数...') # 学生类 class OldboyStudent(OldboyPeople): # school = 'oldboy' # country = 'China' # # def __init__(self, name, age, sex): # self.name = name # self.age = age # self.sex = sex # 学生选择课程 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 class Foo(Goo): X=100 foo_obj = Foo() print(foo_obj.x) # 10 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' objects>, '__weakref__': <attribute '__weakref__' of 'Goo' objects>, '__doc__': None} foo_obj.x = 1000 print('子类的名称空间',Foo.__dict__) # 子类的名称空间 {'__module__': '__main__', 'X': 100, '__doc__': None} 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() # 对象的f1,而非类的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... # 派生后继承关系查找验证: bar_obj = Bar() ''' 结果1: from Foo.f2... from Bar.f1... ''' bar_obj.f2()
- 子类继承父类,派生出自己的属性与方法,并且重用父类的属性与方法。
#方式一: class OldboyPeople: school = 'oldboy' def __init__(self, name, age, sex): # self == tea1, name == 'tank', age == 17, sex == 'male' self.name = name self.age = age self.sex = sex
class OldboyTeacher(OldboyPeople): # tea1, 'tank', 17, 'male', 15000000 def __init__(self, name, age, sex, sal): # self.name = name # self.age = age # self.sex = sex # 类调用类内部的__init__,只是一个普通函数 # OldboyPeople.__init__(tea1, 'tank', 17, 'male') OldboyPeople.__init__(self, name, age, sex) self.sal = sal
def change_score(self): print(f'老师 {self.name} 修改分数...') class OldboyStudent(OldboyPeople): def __init__(self, name, age, sex, girl): OldboyPeople.__init__(self, name, age, sex) self.girl = girl def choose_course(self): print(f'学生 {self.name} 选择课程...') tea1 = OldboyTeacher('tank', 17, 'male', 15000000) print(tea1.name, tea1.age, tea1.sex, tea1.sal) stu1 = OldboyStudent('姚玉鑫', 28, 'male', '凤姐') print(stu1.name, stu1.age, stu1.sex, stu1.girl)
# 方式二: class OldboyPeople: school = 'oldboy' # self == tea1 def __init__(self, name, age, sex): # self == tea1, name == 'tank', age == 17, sex == 'male' self.name = name self.age = age self.sex = sex class OldboyTeacher(OldboyPeople): # tea1, 'tank', 17, 'male', 15000000 def __init__(self, name, age, sex, sal): # super() ---> 特殊的对象 ---> 对象.属性 ---> 父类的名称空间 # 会将调用类传入的对象当做第一个参数传给__init__() super().__init__(name, age, sex) self.sal = sal def change_score(self): print(f'老师 {self.name} 修改分数...') class OldboyStudent(OldboyPeople): def __init__(self, name, age, sex, girl): super().__init__(name, age, sex) self.girl = girl def choose_course(self): print(f'学生 {self.name} 选择课程...') tea1 = OldboyTeacher('tank', 17, 'male', 15000000) print(tea1.name, tea1.age, tea1.sex, tea1.sal) stu1 = OldboyStudent('姚玉鑫', 28, 'male', '凤姐') print(stu1.name, stu1.age, stu1.sex, stu1.girl)
经典类与新式类: (了解)
- 工作中遇不到
- 面试有可能会问
- 新式类:
1.凡是继承object的类或子孙类都是新式类。
2.在python3中所有的类都默认继承object。
- 经典类:
1.在python2中才会有经典类与新式类之分。
2.在python2中,凡是没有继承object的类,都是经典类。
调用mro返回的是一个继承序列: (了解知识点)
super的继承顺序严格遵循mro继承序列。
super严格遵循mro继承顺序
调用mro返回的是一个继承序列: (了解知识点)
super的继承顺序严格遵循mro继承序列。
class Father1: x = 10 pass class Father2: x=20 pass # 多继承的情况下: 从左到右 class Sub(Father1,Father2): # 注意: __int__ 不是 __init__ def __init__(self): print(super().__delattr__) print(Sub.mro()) # 显示继承顺序 # [<class '__main__.Sub'>, <class '__main__.Father1'>, <class '__main__.Father2'>, <class 'object'>] obj=Sub() # <method-wrapper '__delattr__' of Sub object at 0x0000000009B758D0> print(object)
在python3中提供了一个查找新式类查找顺序的内置方法.
mro(): 会把当前类的继承关系列出来。
class Father1: x = 10 pass class Father2: x=20 pass # 多继承的情况下: 从左到右 class Sub(Father1,Father2): # 注意: __int__ 不是 __init__ def __init__(self): print(super().__delattr__) print(Sub.mro()) # 显示继承顺序 # [<class '__main__.Sub'>, <class '__main__.Father1'>, <class '__main__.Father2'>, <class 'object'>] obj=Sub() # <method-wrapper '__delattr__' of Sub object at 0x0000000009B758D0> print(object)
在python3中提供了一个查找新式类查找顺序的内置方法.
mro(): 会把当前类的继承关系列出来。
# 注意: super()会严格按照mro列表的顺序往后查找 class A: def test(self): print('from A.test') super().test() # 调用对象的父级,非A类的父级 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。 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 print(F.mro()) obj=F() obj.test() # from B
继承json模块中JSONEncoder,并派生出新的功能。
import json from datetime import date, datetime # 在原来json模块中可序列化的数据类型优先 # <class 'datetime.datetime'> date # print(type(datetime.now())) # # dict1 = { # # 'time1': str(datetime.now()) # 'time1': datetime.now() # } # # res = json.dumps(dict1) # print(res) # 'datetime' is not JSON serializable class MyJson(json.JSONEncoder): # datetime.now() ---> o def default(self, o): # isinstance: 判断一个对象是否是一个类的实例 if isinstance(o, datetime): # True return datetime.strftime(o, '%Y-%m-%d %X') else: return super().default(self, o) dict1 = { # 'time1': str(datetime.now()) 'time1': datetime.now(), 'name': 'tank' } # 指定自定义的一个MyJson 派生类 # cls=自定义的类 res = json.dumps(dict1, cls=MyJson) print(res) # {"time1": "2019-11-27 14:24:39", "name": "tank"}