day29 继承

一、property装饰器

应用场景1

当我们需要在类中定义一个属性,他本质是一种方法,但是我们要让他用起来像是一种数据

# 例 人类的BMI会随着身高或者体重的变化而变化,所以我们在每次修改身高体重时BMI都会动态的变化,但是Bmi本身又是一种数据,用起来以数据的方式会更清晰
class People:
    def __init__(self, name, weight, height):
        self.name = name
        self.weight = weight
        self.height = height
    @property
    def bmi(self):
        return self.weight // (self.height ** 2)
obj = People('hz',70,1.82)
print(obj.bmi)
>>>21.0

应用场景2

当我们把类中的某个属性隐藏起来后,可以用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('不让删除')
    # 通过property的方法给类的name属性关联查看,修改,删除操作(按顺序)
    name=property(get_name,set_name,del_name)
obj = People('hz')
print(obj.name)
obj.name = 'lxt'
print(obj.name)
del obj.name

应用场景3(场景2优化)

class People:
    def __init__(self, name):
        self.__name = name

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

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

    @name.deleter
    def name(self):
        print('不让删除')


obj = People('hz')
print(obj.name)
obj.name = 'lxt'
print(obj.name)
del obj.name

二、继承介绍

定义

继承是一种创建新类的方式,在Python中,新建的类可以继承一个或多个父类,新建的类可称为子类或派生类,父类又可称为基类或超类

继承
优点:解决类与类之间代码冗余
缺点:将类耦合到一起

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

新式类和经典类

在Python2中有经典类与新式类之分,没有显式地继承object类的类,以及该类的子类,都是经典类,显式地继承object的类,以及该类的子类,都是新式类。而在Python3中,即使没有显式地继承object,也会默认继承该类

1 语法

类是为了减少程序的代码重复,而继承就是为了减少类之间的代码重复

我们要找到类之间的重复关系,就要先抽象出来类的属性,把重复的统一成父类

# 老师和学生都属于人类,可以把他们之间的共同属性提取出来放在人类里,再去继承人类
class People:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex

class Student(People):
    def __init__(self,name,age,sex,sno):
        People.__init__(self,name,age,sex)#继承使用人类的属性
        self.sno = sno
    def choose_course(self,course_obj):
        self.course = course_obj

class Teacher(People):
    def __init__(self,name,age,sex,sarly,level):
        People.__init__(self,name,age,sex)#继承使用人类的属性
        self.sarly = sarly
        self.level = level
    def set_score(self,student_obj,valu):
        print(f'{self.name}给{student_obj.name}打分:{valu}')
        student_obj.score = valu


stu_obj = Student('hz',18,'male',21346)
tea_obj = Teacher('egon',111,'male',2000,10)
tea_obj.set_score(stu_obj,18)
print(stu_obj.score)

2 属性查找

对象的属性查找顺序是:对象本身,类,父类

3 继承的实现原理

3.1 菱形问题

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

这种继承结构下导致的问题称之为菱形问题:如果A中有一个方法,B和/或C都重写了该方法,而D没有重写它,那么D继承的是哪个版本的方法:B的还是C的?如下所示

class A(object):
    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
obj = D()
obj.test() # 结果为:from B

那么为什么是先继承B呢,想知道这个就必须了解python继承实现的原理

3.2 继承原理

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

print(D.mro())
>>>[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。 而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:

1.子类会先于父类被检查

2.多个父类会根据它们在列表中的顺序被检查

3.如果对下一个类存在两个合法的选择,选择第一个父类

3.3 深度优先和广度优先

非菱形结构

当我们的继承是非菱形结构时,不管是新式类还是经典类都是按照分支一个一个找下去最后找object

菱形结构

经典类:深度优先,会在检索第一条分支的时候就直接一条道走到黑,即会检索大脑袋(共同的父类)

新式类:广度优先,会在检索最后一条分支的时候检索大脑袋

posted @ 2020-04-09 15:16  lxttt521  阅读(169)  评论(0编辑  收藏  举报