day29_继承_mor

.1 菱形问题

​ 大多数面向对象语言都不支持多继承,而在Python中,一个子类是可以同时继承多个父类的,这固然可以带来一个子类可以对多个不同父类加以重用的好处,但也有可能引发著名的 Diamond problem菱形问题(或称钻石问题,有时候也被称为“死亡钻石”),菱形其实就是对下面这种继承结构的形象比喻

A类在顶部B类和C类分别位于其下方D类在底部将两者连接在一起形成菱形

这种继承结构下导致的问题称之为菱形问题
python到底是如何实现继承的呢? 对于你定义的每一个类,Python都会计算出一个方法解析顺序(MRO)列表,该MRO列表就是一个简单的所有基类的线性顺序列表

4.2 继承原理

python到底是如何实现继承的呢? 对于你定义的每一个类,Python都会计算出一个方法解析顺序(MRO)列表,该MRO列表就是一个简单的所有基类的线性顺序列表,如下

python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。 而这个MRO列表的构造是通过一个C3线性化算法来实现的。

我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:

1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类

.由对象发起的属性查找,会从对象自身的属性里检索,没有则会按照对象的类.mro()规定的顺序依次找下去,
2.由类发起的属性查找,会按照当前类.mro()规定的顺序依次找下去,

如果继承关系为菱形结构,那么经典类与新式类会有不同MRO,分别对应属性的两种查找方式:深度优先和广度优先

1 当类是经典类时,多继承情况下,在要查找属性不存在时, 以深度优先

2 当类是新式类时,多继承情况下,在要查找属性不存在时, 广度优先

 

# 装饰器实在不修改被装饰对象源代码以及调用方式的前提下为被装饰对象添加新功能的可调用对象
# 通过类的内置属性__bases__可以查看类继承的所有父类
# property 是一个装饰器 ,是 将绑定给对象的方法 伪造成一个数据属性
# 案例二:
class People:
def __init__(self, name):
self.__name = name

def get_name(self):
return self.__name

def set_name(self, val):
if type(val) is not str:
print('必须传入str类型')
return
self.__name = val

def del_name(self):
print('不让删除')
# del self.__name

name=property(get_name,set_name,del_name) #重要
# class C(object):
# def getx(self): return self._x
# def setx(self, value): self._x = value
# def delx(self): del self._x
# x = property(getx, setx, delx, "I'm the 'x' property.")

obj1=People('egon')
# print(obj1.get_name())
# obj1.set_name('EGON')
# print(obj1.get_name())
# obj1.del_name()
# # 人正常的思维逻辑
# -------------------
# print(obj1.name) #
# obj1.name=18
# del obj1.name

# ---------------property
# 案例三:
class People:
def __init__(self, name):
self.__name = name


@property
def name(self): # obj1.name
return self.__name

@name.setter
def name(self, val): # obj1.name='EGON'
if type(val) is not str:
print('必须传入str类型')
return
self.__name = val

@name.deleter #执行del name 会调用这个属性
def name(self): # del obj1.name
print('不让删除')
# del self.__name


# obj1=People('egon')
# # 人正常的思维逻辑
# print(obj1.name) #
# obj1.name=18
# del obj1.name
# 继承 -----------------------
# I:继承是一种创建新类的方式,新建的类可称为子类或派生类,父类又可称为基类或超类,子类会遗传父类的属性
# II:需要注意的是:python支持多继承
# 在Python中,新建的类可以继承一个或多个父类
# 分为 单继承 多继承
# class Parent1():
# pass
# class Sub1(Parent1,): # 多继承
# pass
# #
# print(Sub1.__bases__) #打印所有父类
# print(Sub1.mro()) #打印 mro 顺序
# ---------------
# ps1: 在python2中有经典类与新式类之分 #重要
# 新式类:继承了object类的子类,以及该子类的子类子子类。。。
# 经典:没有继承object类的子类,以及该子类的子类子子类。。。

# ps2:在python3中没有继承任何类,那么会默认继承object类, 所以python3中所有的类都是新式类
# print(Parent1.__bases__)
# print(Parent2.__bases__)
#

# 父类如果不想让子类覆盖自己的方法,可以采用在父类中 双下划线开头的方式将方法设置为私有的

# III:python的多继承
# 优点:子类可以同时遗传多个父类的属性,最大限度地重用代码
# 缺点:
# 1、违背人的思维习惯:继承表达的是一种什么"是"什么的关系
# 2、代码可读性会变差
# 3、不建议使用多继承,有可能会引发可恶的菱形问题,扩展性变差,
# 如果真的涉及到一个子类不可避免地要重用多个父类的属性,应该使用Mixins

# 2、为何要用继承:用来解决类与类之间代码冗余问题

# 示范2:基于继承解决类与类之间的冗余问题
class OldboyPeople:
school = 'OLDBOY'

def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex


class Student(OldboyPeople):
def choose_course(self):
print('学生%s 正在选课' % self.name)
# stu_obj = Student('lili', 18, 'female')
# print(stu_obj.__dict__)
# print(stu_obj.school)
# stu_obj.choose_course()


class Teacher(OldboyPeople):
# 老师的空对象,'egon',18,'male',3000,10
def __init__(self, name, age, sex, salary, level):
# 指名道姓地跟父类OldboyPeople去要__init__
OldboyPeople.__init__(self,name,age, sex) #继承 并继承父类的初始化方法,其它方法类似
self.salary = salary
self.level = level

def score(self):
print('老师 %s 正在给学生打分' % self.name)

tea_obj=Teacher('egon',18,'male',3000,10)
# print(tea_obj.__dict__)
# print(tea_obj.school)
tea_obj.score()
# ------------------------
# 继承查找顺序都遵循 mro() 参照该类的mro列表
# 总结:类相关的属性查找(类名.属性,该类的对象.属性),都是参照该类的mro

# # 二:如果多继承是非菱形继承,经典类与新式的属性查找顺序一样:
# # 都是一个分支一个分支地找下去,然后最后找object


# 三:如果多继承是菱形继承,经典类与新式类的属性查找顺序不一样:
# 经典类:深度优先,会在检索第一条分支的时候就直接一条道走到黑,即会检索大脑袋(共同的父类)
# 新式类:广度优先,会在检索最后一条分支的时候检索大脑袋

# 在python2中,未继承object的类及其子类,都是经典类
posted @ 2021-05-09 18:36  walkerpython  阅读(65)  评论(0编辑  收藏  举报