[面向对象之继承应用(在子类派生重用父类功能(super),继承实现原理(继承顺序、菱形问题、继承原理、Mixins机制),组合]


继承应用

类与类之间的继承指的是什么’是’什么的关系(比如人类,猪类,猴类都是动物类)。子类可以继承/遗传父类所有的属性,因而继承可以用来解决类与类之间的代码重用性问题。比如我们按照定义Student类的方式再定义一个Teacher类

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): # 老师类与学生类功能多一个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与Student之间存在重复的代码,老师与学生都是人类,所以我们可以得出如下继承关系,实现代码重用
  • 在子类派生的新方法中重用父类的功能:

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

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):
# 定义一个__init__函数是形参
# 空对象,'egon',18,"male",10
def __init__(self,name,age,gender,level): # 老师类多一个level就需要保留
# 调用父类的__init__是函数(实参)不是方法,为老师类的__init__进行传参
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__)

方式二: 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__) # 案例:
class A: # [A,object]
def test(self):
print("from A")
super().test()
class B:
def test(self):
print('from B')
class C(A,B): # [C,A,B,object]
pass # obj=C()
# obj.test() obj1 = A()
obj1.test()

继承的实现原理

继承顺序

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中才分新式类与经典类

菱形问题

大多数面向对象语言都不支持多继承,而在Python中,一个子类是可以同时继承多个父类的,这固然可以带来一个子类可以对多个不同父类加以重用的好处,但也有可能引发著名的 Diamond problem菱形问题(或称钻石问题,有时候也被称为“死亡钻石”),菱形其实就是对下面这种继承结构的形象比喻
菱形继承/死亡钻石:一个子类继承的多条分支最终汇聚一个非object的类上
class G: # 在python2中,未继承object的类及其子类,都是经典类
def test(self):
print('from G') class E(G):
def test(self):
print('from E') class F(G):
def test(self):
print('from F') class B(E):
# def test(self):
# print('from B')
pass class C(F):
def test(self):
print('from C') class D(G):
def test(self):
print('from D') class A(B,C,D):
# def test(self):
# print('from A')
pass obj = A() # A->B->E->C->F->D->G->object
# print(A.mro()) obj.test()
  • 继承原理

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

A.mro()  # 等同于A.__mro__
[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class '__main__.G'>, <class 'object'>]
  • 为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。 而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:

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

    继承表达的是一个is-a的关系(什么是什么的关系)

    单继承可以很好的表达人类的逻辑(一个子类继承一个父类符合逻辑且清晰)
    多继承就有点乱,在人类的世界观里,一个物品不可能是多种不同的东西,因此多重继承
    在人类的世界观里是说不通的,它仅仅只是代码层面的逻辑(还可能导致菱形问题)
    民航飞机、直升飞机、轿车都是一个(is-a)交通工具,前两者都有一个功能是飞行fly,但是轿车没有,所以如下所示
    # 我们把飞行功能放到交通工具这个父类中是不合理的
    class Vehicle: # 交通工具
    def fly(self):
    '''
    飞行功能相应的代码
    '''
    print("I am flying") class CivilAircraft(Vehicle): # 民航飞机
    pass class Helicopter(Vehicle): # 直升飞机
    pass class Car(Vehicle): # 汽车并不会飞,但按照上述继承关系,汽车也能飞了
    pass
    Python语言没有接口功能,但Python提供了Mixins机制,简单来说Mixins机制指的是子类混合(mixin)不同类的功能,而这些类采用统一的命名规范(例如Mixin后缀),以此标识这些类只是用来混合功能的,并不是用来标识子类的从属"is-a"关系的,所以Mixins机制本质仍是多继承,但同样遵守”is-a”关系,如下
    class Vehicle:   # 交通工具
    pass class FlyableMixin: # 定义一个飞行功能
    def fly(self):
    print('flying') class CivilAircraft(FlyableMixin,Vehicle): # 民航飞机 继承了Flyablemixin就使用它的功能
    pass class Helicopter(FlyableMixin,Vehicle): # 直升飞机
    pass class Car(Vehicle): # 小汽车 不继承就不使用它的功能,也不影响
    pass # ps: 采用这种编码规范(如命名规范)来解决具体的问题是python惯用的套路,大家都照这种规范来
    # 这种规范叫什么呢?
    # 建议不要用多继承,如果要用到这个继承:把用来表达归属关系的那个类(当然也是最复杂的
    # 那个类)往多继承的最右边去放(Vehicle)。把用来添加功能的类往左边放并改名Mixin为后缀
    # 的名字(FlyableMixin)这种类的特点是这种类里面就只放功能,并且功能的特点是能独立运行
    总结:可以看到,上面的CivilAircraft、Helicopter类实现了多继承,不过它继承的第一个类我们起名
    FlyableMixin,而不是Flyable,这个并不影响功能,但是会告诉后来读代码的人,这个类是一个Mixin类
    单词表示混入(mix-in),这个名字给人一种提示性的效果,只要以Mixin这种命名方式的类就知道这个类只
    是为我这个类来添加功能的,只要继承这个类就说明混合了这个功能
    • 组合

      组合:一个对象的属性值是指向另外一个类的对象,称为类的组合

      组合与继承都是用来解决代码的重用问题的,不同的是:

      继承是一种什么“是”什么的关系
      组合是则一种“有”什么的关系
      比如学校有学生,老师,学生有多门课程,课程有:类别、周期、学费。学生选python课程或linux课程,或多门课程,应该使用组合,如下示例
      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)
      tea2 = Teacher('lxx',38,"male",3) stu1.course = python # 假如规定只有pyhton一门课程就直接等于python,这就叫组合
      stu1.course.tell() # 直接查看课程信息
      stu1.courses = [] # 给学生1添加多门课程
      stu1.courses.append(python) # 给学生1添加python课程
      stu1.courses.append(linux) # 给学生1添加linux课程 # 此时对象stu1集对象独有的属性、Student类中的内容、Course类中的内容于一身(都可以访问到),
      # 是一个高度整合的产物 print(stu1.courses) # 查看学生1所学的多门课程的每一门课程的信息
      for course_obj in stu1.courses:
      course_obj.tell()

[面向对象之继承应用(在子类派生重用父类功能(super),继承实现原理(继承顺序、菱形问题、继承原理、Mixins机制),组合]的更多相关文章

  1. Python之面向对象的组合、多态、菱形问题、子类中重用父类的两种方式

    一.组合 ''' 1.什么是组合 组合就是一个类的对象具备某一个属性,该属性的值是指向另外一个类的对象 2.为何用组合 组合也是用来解决类与类直接代码冗余问题的 3.如何用组合 ''' # 继承减少代 ...

  2. 接口的定义——默认加public abstract默认全局常量;与继承不同,子类可以同时实现多个接口;抽象类实现接口;接口继承接口

    一. 接口的定义 接口中定义的方法,全部都为抽象方法,默认加public abstract 接口中定义的变量,全部为全局常量,默认加public static final 二.与继承不同,子类可以同时 ...

  3. java构造函数是否可继承,以及子类构造函数可否不使用super调用超类构造函数

    问题一:java的构造函数能否被继承? 笔者初学java看的一本书说:“java的子类自然的继承其超类的“非private成员”. 通常java的构造函数被设置为public的(若你不写构造函数,ja ...

  4. Day 5-2 类的继承和派生,重用

    类的继承 派生 在子类中重用父类 组合 抽象类 定义: 继承指的是类与类之间的关系,是一种什么“是”什么的关系,继承的功能之一就是用来解决代码重用问题. 继承是一种创建新类的方式,在python中,新 ...

  5. [Effective JavaScript 笔记]第39条:不要重用父类的属性名

    假设想给上节讲的场景图库添加收集诊断信息的功能.这对于调试和性能分析很有用. 38条示例续 给每个Actor实例一个唯一的标识数. 添加标识数 function Actor(scene,x,y){ t ...

  6. python基础----继承与派生、组合、接口与归一化设计、抽象类、子类中调用父类方法

    一.什么是继承                                                                          继承是一种创建新的类的方式,在pyth ...

  7. python基础之类的继承与派生、组合、接口与归一化设计、抽象类、子类中调用父类方法

    一.什么是继承 继承是一种创建新的类的方式,新建的类可以继承自一个或者多个父类,原始类称为基类或超类,新建的类称为派生类或子类. 派生:子类继承了父类的属性,然后衍生出自己新的属性,如果子类衍生出的新 ...

  8. python基础语法15 面向对象2 继承,多态,继承json模块中JSONEncoder,并派生出新的功能

    继承 1.什么是继承? 继承是一种新建类的方式,新建的类称之为子类或派生类,继承的父类称之为基类或超类. - 在Python中,一个子类可以继承多个父类.(面试可能会问) - 在其它语言中,一个子类只 ...

  9. C++ Pirmer : 第十五章 : 面向对象程序设计之基类和派生的定义、类型转换与继承与虚函数

    基类和派生类的定义以及虚函数 基类Quote的定义: classs Quote { public: Quote() = default; Quote(cosnt std::string& bo ...

随机推荐

  1. Java基础知识-简明阐述双亲委派机制及作用

    1.双亲委派机制及作用 1.1 什么是双亲委派机制 当某个类加载器需要加载某个.class文件时,它首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类 ...

  2. 搞懂 ZooKeeper 集群的数据同步

    本文作者:HelloGitHub-老荀 Hi,这里是 HelloGitHub 推出的 HelloZooKeeper 系列,免费开源.有趣.入门级的 ZooKeeper 教程,面向有编程基础的新手. 项 ...

  3. java例题_14 该日期一年中的第几天问题

    1 /*14 [程序 14 求日期] 2 题目:输入某年某月某日,判断这一天是这一年的第几天? 3 程序分析:以 3 月 5 日为例,应该先把前两个月的加起来,然后再加上 5 天即本年的第几天,特殊情 ...

  4. Android 之 ToolBar 踩坑笔记

    写在前面 •前言 这两天,学完了 Fragment 的基础知识,正准备跟着<第一行代码>学习制作一个简易版的新闻应用: 嘀嘀嘀~~~ 一声消息传来,像往常一样,打开 QQ,当我看到 QQ ...

  5. [状压DP]车

    车 车 车 题目描述 在 n ∗ n n*n n∗n( n ≤ 20 n≤20 n≤20)的方格棋盘上放置 n n n个车(可以攻击所在行.列),有些格子不能放,求使它们不能互相攻击的方案总数. 输入 ...

  6. 【Java】流、IO(初步)

    (这部分比较抽象且写的不是很好,可能还要再编辑) [概述] 流:流是一系列数据,包括输入流和输出流.你可以想象成黑客帝国的"代码雨",只要我们输入指令,这些数据就像水一样流进流出了 ...

  7. BUAA_OS lab4 难点梳理

    BUAA_OS lab4 难点梳理 lab4体会到了OS难度的飞升.实验需要掌握的重点有以下: 系统调用流程 进程通信机制 fork 本lab理解难度较高,接下来将以以上三部分分别梳理. 系统调用 概 ...

  8. Java刷题-tree

    一.分别按照二叉树先序,中序和后序打印所有的节点. 这道题就是书上的算法思想的实际使用,唯一需要特别注意到的是用递归的方式建树,还是比较巧妙的,因为一棵树的建立过程字符流是重复使用的,用递归的方式对根 ...

  9. linux下更新gcc

    1 下载源码 链接 目前最新为9.2版本. 2 解压 tar -zxvf gcc-9.2.0.tar.gz cd gcc-9.2.0 3 download_prerequisites脚本 运行这个脚本 ...

  10. Mariadb3—多表查询

    1.内关联 select 字段名 from 表名1 inner join 表名2 on 表名1.字段名=表名2.字段名 where 条件 2.左关联 select 字段名 from 表名1 left ...