# **************************************************************************
# Python学习
# **************************************************************************
# ** 所属主题: 类
# ** 所属分层: 05 面向对象三大特性--继承
# ** 功能描述: 05 面向对象三大特性--继承
# ** 创 建 者: 陈红伟
# ** 创建日期: 2021/6/19 5:53 下午
# **************************************************************************
# ** 修改日期 修改人 修改内容
# ** 2021/6/19 陈红伟 新增学习内容代码
# **************************************************************************
"""
本章内容:
继承:
单继承,多继承,__bases__(查看类继承关系) , __dict__(查看对象中的属性),
经典类,新式类,派生(重写),菱形继承,深度优先查找,广度优先查找,mro(),Mixins机制
"""
"""
一、什么是继承
1、继承是一种创建新类的方式,新建的子类可称之为子类或者派生类,父类又可以称之为基类或超类,子类会遗传父类的属性
2、需要注意的是python支持多继承
在python中,新建的类可以继承一个或多个父类
"""
class Parent1:
pass
class Parent2:
pass
class Sub1(Parent1): # 单继承
pass
class Sub2(Parent1, Parent2): # 多继承
pass
print(Sub1.__bases__) # (<class '__main__.Parent1'>,)
print(Sub2.__bases__) # (<class '__main__.Parent1'>, <class '__main__.Parent2'>)
"""
Ps:在python2中有金典类和新式类之分:
新式类:继承了object类的子类,以及该类的子类子子类
经典类:没有继承object类的子类,以及该类的子类子子类
Ps:在python3中没有继承任何类,那么会默认继承object类,所以python3中所有的类都叫新式类。
所以为了兼容Python2,我们可以在创建类的时候写上继承object(如果没有要求要继承其他类)
例如:class Person(object)
"""
# 二、python的多继承
# 优点:子类可以同事遗传多个父类的属性,最大限度的重用代码
# 缺点:
# 1、违背人的思维习惯:继承表达的是一种什么是'什么'的关系
# 2、代码可读性变差
# 3、不建议使用多继承,可扩展性太差
# ps:如果真的涉及到一个子类不可避免的要重用多个父类的的属性,应该使用Mixins机制
# 三、如何使用继承: 为了解决类与类之间的冗余问题
# 示范:
class ChwPeople(object):
school_name = 'chw'
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
class Student(ChwPeople):
# school_name = 'chw'
# def __init__(self, name, age, sex):
# self.name = name
# self.age = age
# self.sex = sex
def choose_course(self):
print(f'{self.name}正在选课')
stu1 = Student('s1', 16, '男') # 继承之后当前类找不到就会去父类找
print(stu1.__dict__) # {'name': 's1', 'age': 16, 'sex': '男'}
print(stu1.school_name) # chw
# 尖叫提示:如果父类中有定义了属性和方法,子类中也定义,就叫派生 例如Teacher类的school_name和__init__
class Teacher(ChwPeople):
school_name = 'chw' # 派生
# 派生
def __init__(self, name, age, sex, salary, level):
# self.name = name # 因为该类的基类中定义了这三个属性,所以不需要自己定义
# self.age = age # 因为该类的基类中定义了这三个属性,所以不需要自己定义
# self.sex = sex # 因为该类的基类中定义了这三个属性,所以不需要自己定义
# 所以要指名道姓的跟基类要:
ChwPeople.__init__(self, name, age, sex)
self.salary = salary
self.level = level
def sorce(self):
print(f'{self.name}老师在给学生打分')
t1 = Teacher('李老师', 48, '男', 15000, 'A')
# print(t1.__dict__)
# {'salary': 15000, 'level': 'A'} 【没有加ChwPeople.__init__(self, name, age, sex)这行代码】注释掉后只拿到了两个属性,说明调用的是类Teacher中的init方法
print(t1.__dict__)
# 【加了ChwPeople.__init__(self, name, age, sex)这行代码】 {'name': '李老师', 'age': 48, 'sex': '男', 'salary': 15000, 'level': 'A'}
"""
继承中的菱形继承(死亡钻石)
1、什么是菱形继承:
类:D多继承B和C , B和C都继承A ,A又是一个非object的类。 这种就叫做菱形继承
2、菱形结构带来的问题:
属性查找顺序不一致:【深度优先查找】(一天路走到黑)和【广度优先查找】(最后在找根结点)
【深度优先查找】(金典类用的):沿着一个分支一直找到根结点,在找下一个分支(后面就不会找根结点了,因为已经找过了),直至找到。
【广度优先查找】(新式类用的):沿着一个分支一直找,直到不是根结点,在找下一个分支,直至找到,最后在找根节点。
3、属性查找顺序可以用mro来看: 类名.mro()
ps:【python3才有,所以mro是广度查找,因为python都是新式类,都会继承object】
类的mro(): 类以及类的对象访问属性都是参照该类的mro列表,也就是类中属性查找顺序,但是object一定是最后一个找的
总结:
如果多继承是【非菱形继承】,python2和python3中类的属性查找顺序一样,都是一个一个分支找下去,最后在找object
如果多继承是【菱形继承】,python2(中的经典类)和python3(新式类)中类的属性查找顺序不一样,前者是深度优先查找,后者是广度优先查找
"""
class A:
def test(self):
print("from A")
class B(A):
def test(self):
print("from B")
class C(A):
def test(self):
print("from C")
class D(B, C):
pass
class E(C, B):
pass
"""
类的mro(): 类以及类的对象访问属性都是参照该类的mro列表,也就是类中属性查找顺序,但是object一定是最后一个找的
"""
obj = D()
# 类D以及类D的对象访问属性都是参照该类的mro列表
print(
D.mro()) # [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, # <class 'object'>]
obj.test() # from B
obj = E()
print(
E.mro()) # [<class '__main__.E'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
obj.test() # from C
"""
继承总结:
一、多继承到底又不要用?
要,但是要规避几点问题:
1、继承结构尽量不要过于复杂
2、推荐使用mixins机制:要在多继承的背景下满足继承的什么"是"什么的关系 ==> mixins
"""
"""
多继承的正确打开方式:
mixins机制
核心:就是在多继承背景下尽可能的提升多继承的可读性
"""
# 交通工具类
class Vehicle(object):
def fly(self):
pass
# 民航飞机类
class MinHangFeiJi(Vehicle):
pass
# 直升飞机类
class ZhiShengFeiJi(Vehicle):
pass
# 汽车类
class Car(Vehicle): # 汽车不会非飞,但是按照上述继承关系,他也会飞,所以得新建一个飞行工具的类
pass
print("======这是一条华丽的分割线=======")
# 交通工具类
class Vehicle(object):
pass
# 飞行工具
class FlyableMixin(): # 用Mixin后缀来标识他只是用来记录是属于部分分类的交通工具额外的功能,java中只有单继承,用接口来解决此问题
def fly(self):
pass
# 爬行工具
class PaxingMixin(): # 用Mixin后缀来标识他只是用来记录是属于部分分类的交通工具额外的功能,java中只有单继承,用接口来解决此问题
def pa(self):
pass
# 民航飞机类
class MinHangFeiJi(Vehicle, PaxingMixin):
pass
# 直升飞机类
class ZhiShengFeiJi(Vehicle, PaxingMixin):
pass
# 汽车类
class Car(Vehicle):
pass
"""
在子类派生的新方法中重用父类的功能:
方式一:指名道姓调用某一个类下的函数 ==> 不依赖于继承关系
方式二:super(),调用父类提供给自己的方法 ==> 严格的依赖继承关系
调用super()会得到一个特殊的对象,该对象会参照当前类的mro,去当前类的父类中找属性
尖叫提示⚠️:super是根据发起属性查找的那个类的mro。去当前类中找属性
"""
# 方式一、指名道姓调用某一个类下的函数
class C1:
def __init__(self, name):
self.name = name
def f1(self):
print('C1 f1')
class B1(C1):
def __init__(self, name, b1, b2):
C1.__init__(self, name) # 方式一:指名道姓调用某一个类下的函数
self.b1 = b1
self.b2 = b2
b1 = B1('b1', 1, 1)
print(b1.__dict__) # {'name': 'b1', 'b1': 1, 'b2': 1}
# 方式二、super(),调用父类提供给自己的方法
class C1:
def __init__(self, name):
self.name = name
def f1(self):
print('C1 f1')
class B1(C1):
def __init__(self, name, b1, b2):
# C1.__init__(self, name) # 方式一:指名道姓调用某一个类下的函数
# super().__init__(name) # python2写法:调用的是方法,自动传入对象
super(B1, self).__init__(name) # python3写法:调用的是方法
self.b1 = b1
self.b2 = b2
b1 = B1('b1', 1, 1)
print(b1.__dict__) # {'name': 'b1', 'b1': 1, 'b2': 1}
# 特别提醒⚠️:
# 尖叫提示⚠️:super是根据发起属性查找的那个类的mro。去当前类中找属性
class A:
def test(self):
print('A test')
class B:
def test(self):
print('B test')
class C(A,B):
pass
c = C()
c.test() # A test # 广度查找
class A:
def test(self):
print('A test')
super(A, self).test1()
class B:
def test(self):
print('B test')
# def test1(self):
# print('B test1')
class C(A,B):
def test1(self):
print("C test1")
c = C()
print(C.mro()) # [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
# c.test() # AttributeError: 'super' object has no attribute 'test1'