目录
面向对象阶段最重要的知识点:
面向对象的三大特性
- 继承(组合)
- 封装
- 多态
继承(灵魂三拷问)
什么是继承?
继承指的是定义类的方法,新定义的类称之为子类或者派生类
子类继承的类叫做父类,也叫基类/超类
继承的特性:
- 子类可以继承父类的属性(特征与技能)
- 并且可以派生出自己的属性(特征和技能)
- 在python中一个子类可以继承多个父类,其他语言只能继承一个父类
为什么要继承
减少代码的冗余(减少重复代码)。
如何实现继承
首先确定好谁是子类,谁是父类
在定义类时,子类(),()内写上父类名,实现继承
继承初体验:
# 父类1 class ParentClass1: pass # 父类2 class ParentClass2: pass # 子类1 class SubClass1(ParentClass1): # 继承父类1 pass # 子类2 class SubClass2(ParentClass1,ParentClass2): # 继承父类1,父类2 pass # 查看继承的父类:__bases__是类的属性,用来查找当前类的父类 print(SubClass1.__bases__) # (<class '__main__.ParentClass1'>,) print(SubClass2.__bases__) # (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
寻找继承关系
如何寻找继承关系
要想寻找继承关系,首先要“先抽象,在继承”
抽象与继承
先抽象
抽象是一种思想
奥巴马 --> 属于人类 --> 属于动物类
哈士奇 --> 属于狗类 --> 属于动物类
把相同的属性(特征和技能)抽象出来,定义动物类,称之为父类。
动物类:
特征:
眼睛、鼻子、耳朵
技能:
吃、喝、拉、撒
再继承
继承在程序中实现
奥巴马 (对象)--> 调用人类 --> 继承动物类
哈士奇 (对象)--> 调用狗类 --> 继承动物类
继承的关系
- 对象是特征与技能的结合体.
- 类是一系列对象相同的特征与技能的结合体.
- 继承是一系列类相同的特征与技能的结合体.
上代码
# 父类 class OldboyPeople: # 定义相同的属性 school = "oldboy" 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("qinyj",18,"female") tea1 = OldboyTeacher("tank",18,"female") print(stu1.name,stu1.age,stu1.sex) print(tea1.name,tea1.age,tea1.sex)
继承下对象属性查找顺序
在继承背景下,对象属性的查找顺序为:
对象查找属性会先从对象的名称呢过空间中查找
若对象中没有,则会去类里面查找
若当前子类里面有就返回,如果没有会去父类里面找
注意:若子类定义和父类相同的属性,会优先使用子类的。
# 验证对象属性的查找顺序: class Foo: def f1(self): print("Foo.f1") def f2(self): print("Foo.f2") self.f1() class Soo(Foo): def f1(self): print("Soo.f1") s = Soo() s.f2() # Foo.f2 # Soo.f1 # Soo-->Foo 在Soo类中重新定义了f1方法,此时优先使用子类中的f1方法,这时候对象去子类的名称空间里找就会找到,打印Soo.f1 # 查看对象名称空间 __dict__ print(s.__dict__) # __class__:查看对象的属性,查看当前对象的类 print(s.__class__) # <class '__main__.Soo'> # 查看子类名称空间 print(s.__class__.__dict__) # {'__module__': '__main__', 'f1': <function Soo.f1 at 0x0000000009FF3BF8>, '__doc__': None} # 查看父类的名称空间 # __bases__:查看继承的父类 print(s.__class__.__bases__[0].__dict__) # {'__module__': '__main__', 'f1': <function Foo.f1 at 0x0000000009FF3B70>, 'f2': <function Foo.f2 at 0x0000000009FF3BF8>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}
派生
什么是派生
- 派生指的是子类继承父类的属性,并且派生出新的属性
- 子类派生出新的属性,若与父类属性相同,则调用使用子类的
- 继承指的是类与类的关系,子类与父类是从属的关系
子类派生新的属性并重用父类的属性
方式一:
直接调用父类的的__init__(self)
方法,把__init__(self)
当做普通函数使用,传入对象与继承的属性
方式二:
使用super函数,super是一个特殊的类,在子类中调用super()会得到一个特殊的对象,通过“.” 指向父类的名称空间,将本身传入__init__(self)
函数当中的一个参数
注意:两种方式不要混合使用。
class OldboyPeople: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex # 方式一: # 直接调用父类的__init__(self)函数 class OldboyTeacher(OldboyPeople): def __init__(self,name,age,sex,level): OldboyPeople.__init__(self,name,age,sex) self.level = level class OldboyStudent(OldboyPeople): def __init__(self,name,age,sex,course): OldboyPeople.__init__(self, name, age, sex) self.course = course stu1 = OldboyStudent("qinyj",18,"man","python") tea1 = OldboyTeacher("tank",18,"man","10") print(tea1.name,tea1.level) print(stu1.name,stu1.course) # 方式二: # 使用super()函数 class OldboyTeacher(OldboyPeople): def __init__(self,name,age,sex,level): super().__init__(name,age,sex) self.level = level class OldboyStudent(OldboyPeople): def __init__(self,name,age,sex,course): super().__init__(name, age, sex) self.course = course stu1 = OldboyStudent("qinyj",18,"man","python") tea1 = OldboyTeacher("tank",18,"man","10") print(tea1.name,tea1.level) print(stu1.name,stu1.course)
新式类与经典类
继承了 object 类的是新式类,没有继承的是经典类
新式类:python3中都是新式类,在python3中默认继承object类
经典类:python2中凡是没有继承object类的都是经典类
mro函数
mro函数属于object类,在多继承情况下,用来查看当前类的继承顺序的
class A: x = 2 pass class B: x = 3 pass class C(A,B): x = 1 pass # mro函数 print(C.mro()) # [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>] # 继承顺序: # 先在自己类中找--》A--》B--》object c = C() print(c.x)
钻石继承(菱形继承)
在多继承的情况下形成的钻石继承
针对于新式类和经典类而言:
经典类:深度优先
新式类:广度优先
验证:
# coding=utf-8 ### 新式类继承 # 继承顺序:F-D->B->E->->C->A->object-->若没有报错 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 = F() f.test() ### 经典类继承 # 继承顺序:F->D->B->A->E->C-->若没有报错 class A: # 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 = F() f.test()
实战-通过继承实现修改json模块支持的数据类型
import json from datetime import datetime,date print(json.JSONEncoder) ''' json 支持的python数据类型 +-------------------+---------------+ | Python | JSON | +===================+===============+ | dict | object | +-------------------+---------------+ | list, tuple | array | +-------------------+---------------+ | str | string | +-------------------+---------------+ | int, float | number | +-------------------+---------------+ | True | true | +-------------------+---------------+ | False | false | +-------------------+---------------+ | None | null | +-------------------+---------------+ ''' print(datetime.today()) print(date.today()) # 开发者的角度来说,我们想要把date.today() 或者datetime.today() 当成json 的value值保存为json文件 # 但json数据类型只支持字符串,那么我们可以这么做,将执行结果强转为str dic = { "name":"qinyj", "today":str(date.today()) } print(json.dumps(dic)) # {"name": "qinyj", "today": "2019-10-10"} # 我们从源码角度来看,有可能以后会自己修改源码 class MyJson(json.JSONEncoder): # 子类重新派生出来default功能,优先用子类的 def default(self, o): if isinstance(o,date): return o.strftime("%Y-%m-%d %X") else: # 不满足条件还是继承父类的default方法的功能 return super().default(self,o) dic = { "name":"qinyj", "today":date.today() } # isinstance() :判断一个对象是否是一个已知的类型 print(isinstance(dic,date)) print(isinstance(dic.get("today"),date)) print(json.dumps(dic,cls=MyJson)) # 默认cls=None,默认指向的是原json的JSONEncoder # {"name": "qinyj", "today": "2019-10-10 00:00:00"}