继承的含义

面向对象中继承是用来描述类与类之间数据的从属关系,通过继承可以减少代码冗余,提升开发效率,同样也支持多继承

基本使用

  1. class A: # 被继承的类称为父类或者基类
  2. name = 'from A'
  3. class B(A): # 继承别人的类称为子类或者派生类
  4. pass
  5. print(B.name)
  6. # from A
  7. """在python中一个类可以同时继承多个父类"""
  8. class A:
  9. nameA = 'from A'
  10. class B:
  11. nameB = 'from B'
  12. class C:
  13. nameC = 'from C'
  14. class MyClass(A, B, C):
  15. pass
  16. print(MyClass.nameA)
  17. # from A
  18. print(MyClass.nameB)
  19. # from B
  20. print(MyClass.nameC)
  21. # from C

继承的本质

  1. class Person: # 多个类相同数据和功能的结合体,父类
  2. def __init__(self, name, age, gender):
  3. self.name = name
  4. self.age = age
  5. self.gender = gender
  6. class Teacher(Person): # 多个对象相同数据和功能的结合体,子类
  7. def teach(self):
  8. print(f'{self.name}老师正在讲课')
  9. class Student(Person): # 多个对象相同数据和功能的结合体,子类
  10. def study(self):
  11. print(f'{self.name}正在在学习')
  12. obj_1 = Student('kevin', 22, 'male') # 数据与功能的结合体,对象
  13. obj_1.study()
  14. # kevin正在在学习

继承的分类

python2存在经典类与新式类

  • 经典类:在 python2.2 之前⼀直使⽤的是经典类,经典类在基类的根什么都不写
  • 新式类: 在 python2.2 之后出现了新式类,新式类的特点是基类的根,object
  1. class A:
  2. pass
  3. class B(object):
  4. pass
  5. print(A.__base__)
  6. # 空的
  7. print(B.__base__)
  8. # <class 'object'>

python3只有新式类

  • 新式类:python3中使⽤的都是新式类,如果基类谁都不继承,那这个类会默认继承 object
  1. # 有时候我们在定义类的时候会习惯性的写
  2. class MyClass(object):
  3. pass
  4. # 为了兼容python2和python3

名字的查找顺序(继承)

核心:先从对象自己的名称空间中查找,没有择取产生对象的类中查找,如果还没有并且类有父类则去父类中查找,以此往复下去

  1. 对象 >>> >>> 父类

记住一点,名字的查找顺序永远都是,先从当前对象自身开始查找

单继承

  1. class A:
  2. def func_1(self):
  3. print('from A.func_1')
  4. def func_2(self):
  5. print('from A.func_2')
  6. class MyClass(A):
  7. def func_1(self):
  8. print('from MyClass.func_3')
  9. obj = MyClass()
  10. obj.func_1() # 先找自己,在找继承
  11. # from MyClass.func_3
  12. obj.func_2() # 自己没有,在找继承
  13. # from A.func_2
  1. class A:
  2. name = 'from A'
  3. class B(A):
  4. name = 'from B'
  5. class C(B):
  6. name = 'from C'
  7. class MyClass(C):
  8. name = 'from MyClass' # 有则找MyClass里面的name
  9. obj = MyClass()
  10. obj.name = '我自己的name'
  11. print(obj.name) # 先找自己,在找调用的类,类里没有,在找父类一直往上推
  12. # 我自己的name

面向对象之继承 - 图1

多继承

普通多继承

  1. class A:
  2. # name = 'a'
  3. pass
  4. class B:
  5. name = 'b'
  6. class C:
  7. name = 'c'
  8. class MyClass(A,B,C):
  9. # name = 'MyClass'
  10. pass
  11. obj = Myclass()
  12. print(obj.name)
  13. # b

非菱形多继承

父类中名字的查找顺序按照继承时从左往右依次查找,如果多个父类还有分类,那么遵循”深度优先”

  1. class E:
  2. name = 'from E'
  3. class F:
  4. # name = 'from F'
  5. pass
  6. class D:
  7. # name = 'from D'
  8. pass
  9. class A(D):
  10. # name = 'from A'
  11. pass
  12. class B(E):
  13. # name = 'from B'
  14. pass
  15. class C(F):
  16. # name = 'from C'
  17. pass
  18. class MyClass(A,B,C):
  19. # name = 'from MyClass'
  20. pass
  21. obj = MyClass()
  22. # obj.name = 'from obj'
  23. print(obj.name)

面向对象之继承 - 图2

菱形多继承

父类中名字的查找顺序就是按照继承时从左往右依次查找,如果多个父类还有分类 那么遵循”广度优先”

  1. class M:
  2. name = 'from M'
  3. class E(M):
  4. name = 'from E'
  5. pass
  6. class F(M):
  7. name = 'from F'
  8. pass
  9. class D(M):
  10. # name = 'from D'
  11. pass
  12. class A(D):
  13. # name = 'from A'
  14. pass
  15. class B(E):
  16. name = 'from B'
  17. pass
  18. class C(F):
  19. # name = 'from C'
  20. pass
  21. class MyClass(A,B,C):
  22. # name = 'from MyClass'
  23. pass
  24. obj = MyClass()
  25. print(obj.name)
  26. # from B

面向对象之继承 - 图3

派生类用法(子类)

在写的子类需要使用父类的方法,并且还需要基于该方法做扩展,这样的子类称为派生类(本质还是子类),使用关键字super关键字来实现

  1. class Person:
  2. def __init__(self, name, age, gender):
  3. self.name = name
  4. self.age = age
  5. self.gender = gender
  6. class Teacher(Person):
  7. def __init__(self, name, age, gender, salary, hobby):
  8. # 用了Person类里面的__init__方法
  9. # super(Teacher,self).__init__(name, age, gender) # 子类调用父类的完整语法
  10. super().__init__(name, age, gender) # 子类调用
  11. # 自己还要添加一个额外的东西
  12. self.salary = salary
  13. self.salary = hobby
  14. class Student(Person):
  15. def __init__(self, name, age, gender, stu_id, class_id):
  16. # 用了Person类里面的__init__方法
  17. super().__init__(name, age, gender)
  18. # 自己还要添加一个额外的东西
  19. self.stu_id = stu_id
  20. self.class_id = class_id
  21. obj_1 = Teacher('kevin', 22, 'male', 1000, 'run')
  22. obj_2 = Student('kevin', 22, 'male', 123, 123)
  23. print(obj_1.__dict__)
  24. # {'name': 'kevin', 'age': 22, 'gender': 'male', 'salary': 'run'}
  25. print(obj_2.__dict__)
  26. # {'name': 'kevin', 'age': 22, 'gender': 'male', 'stu_id': 123, 'class_id': 123}

派生功能前瞻

  1. class MyClass(list):
  2. def append(self, args):
  3. if args == 123:
  4. print("数字123不能追加")
  5. return
  6. super(MyClass, self).append(args)
  7. print(f"{self}")
  8. obj = MyClass()
  9. obj.append(111)
  10. # [111]
  11. obj.append(222)
  12. # [111, 222]
  13. obj.append(123)
  14. # 数字123不能追加
  15. obj.append(333)
  16. # [111, 222, 333]

派生实际应用

前言

json不能序列化python所有的数据类型,只能是一些基本数据类型,通过继承的方式将,研究并重写json序列化方法

  1. import datetime
  2. import json
  3. times = datetime.date.today()
  4. print(type(times))
  5. # <class 'datetime.date'>
  6. json.dumps(times)
  7. # TypeError: Object of type 'date' is not JSON serializable

研究

研究函数dumps参数中有一个叫cls,如果这个参数为None则等于JSONEncoder ,查看后发现JSONEncoder是一个类,类里有一个方法叫default,正是报错的原因

面向对象之继承 - 图4

面向对象之继承 - 图5

实现

写一个类去继承JSONEncoder,然后重新default方法

  1. import datetime
  2. import json
  3. class MyJsonEncoder(json.JSONEncoder):
  4. def default(self, o):
  5. if isinstance(o, datetime.datetime):
  6. return o.strftime('%Y-%m-%d %X')
  7. elif isinstance(o, datetime.date):
  8. return o.strftime('%Y-%m-%d')
  9. return super().default(o) # 调用父类的default(让父类的default方法继续执行,防止有其他额外操作)
  10. d = {'t1': datetime.datetime.today(), 't2': datetime.date.today()}
  11. res = json.dumps(d, cls=MyJsonEncoder) # 指定cls执行为我们写的类
  12. print(res)
  13. # {"t1": "2022-04-09 17:33:36", "t2": "2022-04-09"}