继承的应用,继承实现原理,super(),多继承的代码规范,组合

一、继承的应用

继承的应用:
对于你定义的每一个类,Python都会计算出一个方法解析顺序(MRO)列表,该MRO列表就是一个简单的所有基类的线性顺序列表,
MRO列表的构造是通过一个C3线性化算法来实现的,我们无需深究该算法的数学原理,它实际上就是合并所有父类的MRO列表,且在查找属性时,
Python会基于MRO列表按照从左到右的顺序依次查找基类,直到找到第一个匹配这个属性的类为止。

在Python中子类可以同时继承多个父类,在子类继承了多个父类时,经典类与新式类会有不同MRO,分别对应属性的两种查找方式:深度优先和广度优先

class Student:
    school = "虹桥校区"

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

    def choose(self):
        print("%s 选课成功" %self.name)

stu1 = Student("jack",18,"male")
stu2 = Student("tom",19,"male")
stu3 = Student('lili',29,"female")

class Teacher:
    school = "虹桥校区"

    def __init__(self,name,age,gender,level):
        self.name = name
        self.age = age
        self.gender = gender
        self.level = level

    def score(self):
        print("%s 正在为学生打分" %self.name)

tea1 = Teacher('egon',18,"male",10)
tea2 = Teacher('lxx',38,"male",3)

在子类派生的新方法中重用父类的功能:

方式一: 指名道姓地引用某一个类的函数,与继承无关

子类可以派生出自己新的属性,在进行属性查找时,子类中的属性名会优先于父类被查找,例如每个老师还有职称这一属性,我们就需要在Teacher类中定义该类自己的--init--覆盖父类的

class People:
    school = "虹桥校区"

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

class Student(People):
    def choose(self):
        print("%s 选课成功" %self.name)

class Teacher(People):
    #            空对象,'egon',18,"male",10
    def __init__(self,name,age,gender,level):
        People.__init__(self,name,age,gender)

        self.level = level

    def score(self):
        print("%s 正在为学生打分" %self.name)

stu1 = Student("jack",18,"male")
stu2 = Student("tom",19,"male")
stu3 = Student('lili',29,"female")


tea1 = Teacher('egon',18,"male",10)  # 空对象,'egon',18,"male",10
tea2 = Teacher('lxx',38,"male",3)

# print(stu1.school)
# print(stu1.name)
# print(stu1.age)
# print(stu1.gender)
# print(tea1.__dict__)

二、继承实现原理

1 继承顺序

在Java和C#中子类只能继承一个父类,而Python中子类可以同时继承多个父类,如A(B,C,D)

如果继承关系为非菱形结构,则会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支的顺序直到找到我们想要的属性

如果继承关系为菱形结构,那么属性的查找方式有两种,分别是:深度优先和广度优先

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):
    def test(self):
        print('from D')

class E(C):
    def test(self):
        print('from E')

class F(D,E):
    # def test(self):
    #     print('from F')
    pass
f1=F()
f1.test()
print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性

#新式类继承顺序:F->D->B->E->C->A
#经典类继承顺序:F->D->B->A->E->C
#python3中统一都是新式类
#pyhon2中才分新式类与经典类

继承顺序

2 继承原理(python如何实现的继承)

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

>>> F.mro() #等同于F.__mro__
[<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类

三、super()

调用super()会得到一个特殊的对象,该对象专门用来引用父类的属性,且严格按照MRO规定的顺序向后查找

class People:
    school = "虹桥校区"

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

class Teacher(People):
    #            空对象,'egon',18,"male",10
    def __init__(self,name,age,gender,level):
        # People.__init__(self,name,age,gender)
        super(Teacher,self).__init__(name,age,gender)

        self.level = level

    def score(self):
        print("%s 正在为学生打分" %self.name)


tea1 = Teacher('egon',18,"male",10)  # 空对象,'egon',18,"male",10
print(tea1.__dict__)

super()是依赖于继承的,并且即使没有直接继承关系,super()仍然会按照MRO继续往后查找

super()案例:

>>> #A没有继承B
... class A:
...     def test(self):
...         super().test()
... 
>>> class B:
...     def test(self):
...         print('from B')
... 
>>> class C(A,B):
...     pass
... 
>>> C.mro() # 在代码层面A并不是B的子类,但从MRO列表来看,属性查找时,就是按照顺序C->A->B->object,B就相当于A的“父类”
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>,<class ‘object'>]
>>> obj=C()
>>> obj.test() # 属性查找的发起者是类C的对象obj,所以中途发生的属性查找都是参照C.mro()
from B

四、多继承的代码规范

Pyton Mixins机制

一个子类可以同时继承多个父类,这样的设计常被人诟病,一来它有可能导致可恶的菱形问题,二来在人的世界观里继承应该是个”is-a”关系。 比如轿车类之所以可以继承交通工具类,是因为基于人的世界观,我们可以说:轿车是一个(“is-a”)交通工具,而在人的世界观里,一个物品不可能是多种不同的东西,因此多重继承在人的世界观里是说不通的,它仅仅只是代码层面的逻辑。不过有没有这种情况,一个类的确是需要继承多个类呢?

答案是有,我们还是拿交通工具来举例子:

民航飞机、直升飞机、轿车都是一个(is-a)交通工具,前两者都有一个功能是飞行fly,但是轿车没有,所以如下所示我们把飞行功能放到交通工具这个父类中是不合理的,加上mixin飞的功能:

class Vehicle:
    pass

class FlyableMixin:   #加上飞行功能
    def fly(self):
        print('flying')

class CivilAircraft(FlyableMixin,Vehicle):  #民航飞机,加 飞的功能放在父类的左边
    pass

class Helicopter(FlyableMixin,Vehicle):  # 直升飞机,加上飞的功能
    pass

class Car(Vehicle): #汽车
    pass

五、组合

在一个类中以另外一个类的对象作为数据属性,称为类的组合。组合与继承都是用来解决代码的重用性问题。不同的是:继承是一种“是”的关系,比如老师是人、学生是人,当类之间有很多相同的之处,应该使用继承;而组合则是一种“有”的关系,比如老师有生日,老师有多门课程,当类之间有显著不同,并且较小的类是较大的类所需要的组件时,应该使用组合

简述--组合: 一个对象的属性值是指向另外一个类的对象

class People:
    school = "虹桥校区"

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

class Student(People):
    def choose(self):
        print("%s 选课成功" %self.name)

class Teacher(People):
    #            空对象,'egon',18,"male",10
    def __init__(self,name,age,gender,level):
        People.__init__(self,name,age,gender)

        self.level = level

    def score(self):
        print("%s 正在为学生打分" %self.name)

class Course:
    def __init__(self,name,price,period):
        self.name = name
        self.price = price
        self.period =period

    def tell(self):
        print('课程信息<%s:%s:%s>' %(self.name,self.price,self.period))

python = Course("python全栈开发",19800,"6mons")
linux = Course("linux",19000,"5mons")

stu1 = Student("jack",18,"male")
stu2 = Student("tom",19,"male")
stu3 = Student('lili',29,"female")


tea1 = Teacher('egon',18,"male",10)  # 空对象,'egon',18,"male",10
tea2 = Teacher('lxx',38,"male",3)


stu1.courses = []
stu1.courses.append(python)
stu1.courses.append(linux)

print(stu1.courses)
for course_obj in stu1.courses:
    course_obj.tell()
posted @ 2021-01-12 19:00  小绵  阅读(110)  评论(0编辑  收藏  举报