面向对象之继承

1.什么是面向对象的继承

继承(英语:inheritance)是面向对象软件技术当中的一个概念。

通俗易懂的理解是:子承父业,合法继承家产

专业的理解是:子类可以完全使用父类的方法和属性

如果一个类A"继承自"另一个类B,则把类A称为类B的子类(或派生类),把类B称为父类(或超类或基类)

继承可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码。在令子类继承父类的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类的原有属性和方法,使其获得与父类不同的功能。

  1. class Person:
  2. def __init__(self,name,sex,age):
  3. self.name = name
  4. self.age = age
  5. self.sex = sex
  6. class Cat:
  7. def __init__(self,name,sex,age):
  8. self.name = name
  9. self.age = age
  10. self.sex = sex
  11. class Dog:
  12. def __init__(self,name,sex,age):
  13. self.name = name
  14. self.age = age
  15. self.sex = sex
  16. # 继承的用法:
  17. class Aniaml(object):
  18. def __init__(self,name,sex,age):
  19. self.name = name
  20. self.age = age
  21. self.sex = sex
  22. class Person(Aniaml):
  23. pass
  24. class Cat(Aniaml):
  25. pass
  26. class Dog(Aniaml):
  27. pass

继承的优点:
1.增加类的耦合性(耦合性不宜多,宜精,软件的开发要遵循高内聚,低耦合原则)

2.减少重复的代码

3.使得代码更加规范化,合理化

2.继承的分类

Animal在上面的代码中叫做 父类,基类,超类

Person Cat Dog : 子类,派生类

继承分为单继承,多继承

python中类的种类:

​ 在python2x版本中存在两种类:

​ 1.经典类:在python2.2之前,一直使用的是经典类,经典类在基类中的class定义的类名后什么都不写(即没有括号和内容)

​ 2.新式类:在python2.2之后出现新式类,新式类的特点是基类继承最顶级类object类

​ python3.x版本只出现一种类:新式类

​ python3如果基类谁都不继承,那么这个类会默认继承object类.

3.单继承

1.类名,对象执行父类方法

  1. class Aniaml(object):
  2. type_name = '动物类'
  3. def __init__(self,name,sex,age):
  4. self.name = name
  5. self.age = age
  6. self.sex = sex
  7. def eat(self):
  8. print(self)
  9. print('吃东西')
  10. class Person(Aniaml):
  11. pass
  12. class Cat(Aniaml):
  13. pass
  14. class Dog(Aniaml):
  15. pass
  16. # 类名:
  17. print(Person.type_name) # 可以调用父类的属性,方法。
  18. Person.eat(111)
  19. print(Person.type_name)
  20. # 对象:
  21. # 实例化对象
  22. p1 = Person('章超印','男',18)
  23. print(p1.__dict__)
  24. # 对象执行类的父类的属性,方法。
  25. print(p1.type_name)
  26. p1.type_name = '666'
  27. print(p1)
  28. p1.eat()
  29. 类名,对象分别调用父类方法

2.执行顺序

  1. class Aniaml(object):
  2. type_name = '动物类'
  3. def __init__(self,name,sex,age):
  4. self.name = name
  5. self.age = age
  6. self.sex = sex
  7. def eat(self):
  8. print(self)
  9. print('吃东西')
  10. class Person(Aniaml):
  11. def eat(self):
  12. print('%s 吃饭'%self.name)
  13. class Cat(Aniaml):
  14. pass
  15. class Dog(Aniaml):
  16. pass
  17. p1 = Person('周道镕','男',18)
  18. # 实例化对象时必须执行__init__方法,类中没有,从父类找,父类没有,从object类中找。
  19. p1.eat()
  20. # 先要执行自己类中的eat方法,自己类没有才能执行父类中的方法。
  21. 执行顺序

3.同时执行类及父类的方法

父类想用父类的func方法,但又想让子类也执行自己的同名方法

方法1:

父类.func(对象,其他参数)

  1. class Aniaml(object):
  2. type_name = '动物类'
  3. def __init__(self,name,sex,age):
  4. self.name = name
  5. self.age = age
  6. self.sex = sex
  7. def eat(self):
  8. print('吃东西')
  9. class Person(Aniaml):
  10. def __init__(self,name,sex,age,mind):
  11. '''
  12. self = p1
  13. name = '周道镕'
  14. sex = 'laddboy'
  15. age = 20
  16. mind = '有思想'
  17. '''
  18. # Aniaml.__init__(self,name,sex,age) # 方法一
  19. self.mind = mind
  20. def eat(self):
  21. super().eat()
  22. print('%s 吃饭'%self.name)
  23. class Cat(Aniaml):
  24. pass
  25. class Dog(Aniaml):
  26. pass
  27. # 方法一: Aniaml.__init__(self,name,sex,age)
  28. # p1 = Person('周道镕','Handsome Girl',55,'有思想')
  29. # print(p1.__dict__)
  30. # 对于方法一如果不理解:
  31. # def func(self):
  32. # print(self)
  33. # self = 3
  34. # func(self)

方法2:

利用super super(父类名,self).func(参数) == super().func(参数)

  1. class Aniaml(object):
  2. type_name = '动物类'
  3. def __init__(self,name,sex,age):
  4. self.name = name
  5. self.age = age
  6. self.sex = sex
  7. def eat(self):
  8. print('吃东西')
  9. class Person(Aniaml):
  10. def __init__(self,name,sex,age,mind):
  11. '''
  12. self = p1
  13. name = '周道镕'
  14. sex = 'Handsome Girl'
  15. age = 55
  16. mind = '有思想'
  17. '''
  18. # Aniaml.__init__(self,name,sex,age) # 方法一
  19. self.mind = mind
  20. def eat(self):
  21. super().eat()
  22. print('%s 吃饭'%self.name)
  23. class Cat(Aniaml):
  24. pass
  25. class Dog(Aniaml):
  26. pass
  27. # 方法一: Aniaml.__init__(self,name,sex,age)
  28. # p1 = Person('周道镕','Handsome Girl',55,'有思想')
  29. # print(p1.__dict__)
  30. # 对于方法一如果不理解:
  31. # def func(self):
  32. # print(self)
  33. # self = 3
  34. # func(self)

4.多继承

  1. class ShenXian: # 神仙
  2. def fei(self):
  3. print("神仙都会⻜")
  4. class Monkey: # 猴
  5. def chitao(self):
  6. print("猴⼦喜欢吃桃⼦")
  7. class SunWukong(ShenXian, Monkey): # 孙悟空是神仙, 同时也是⼀只猴
  8. pass
  9. swk = SunWukong() # 孙悟空
  10. swk.chitao() # 会吃桃⼦
  11. swk.fei() # 会⻜

此时, 孙悟空是⼀只猴⼦, 同时也是⼀个神仙. 那孙悟空继承了这两个类. 孙悟空⾃然就可以执⾏这两个类中的⽅法. 多继承⽤起来简单. 也很好理解. 但是多继承中, 存在着这样⼀个问题. 当两个⽗类中出现了重名⽅法的时候. 这时该怎么办呢? 这时就涉及到如何查找⽗类⽅法的这么⼀个问题.即MRO(method resolution order) 问题. 在python中这是⼀个很复杂的问题. 因为在不同的python版本中使⽤的是不同的算法来完成MRO的.

即在经典类和新式类的多继承算法是不同的.

4.1经典类的多继承

虽然在python3中已经不存在经典类了. 但是经典类的MRO最好还是学⼀学. 这是⼀种树形结构遍历的⼀个最直接的案例. 在python的继承体系中. 我们可以把类与类继承关系化成⼀个树形结构的图

  1. class A:
  2. pass
  3. class B(A):
  4. pass
  5. class C(A):
  6. pass
  7. class D(B, C):
  8. pass
  9. class E:
  10. pass
  11. class F(D, E):
  12. pass
  13. class G(F, D):
  14. pass
  15. class H:
  16. pass
  17. class Foo(H, G):
  18. pass

这种mro画图就可以:

继承关系图已经有了. 那如何进⾏查找呢? 记住⼀个原则. 在经典类中采⽤的是深度优先,遍历⽅案. 什么是深度优先. 就是⼀条路走到头. 然后再回来. 继续找下⼀个.

图中每个圈都是准备要送鸡蛋的住址. 箭头和⿊线表⽰线路. 那送鸡蛋的顺序告诉你入⼝在最下⾯R. 并且必须从左往右送. 那怎么送呢?

如图. 肯定是按照123456这样的顺序来送. 那这样的顺序就叫深度优先遍历. ⽽如果是142356呢? 这种被称为⼴度优先遍历. 好了. 深度优先就说这么多. 那么上⾯那个图怎么找的呢? MRO是什么呢? 很简单. 记住. 从头开始. 从左往右. ⼀条路跑到头, 然后回头. 继续⼀条路跑到头. 就是经典类的MRO算法.

类的MRO: Foo-> H -> G -> F -> E -> D -> B -> A -> C. 你猜对了么?

4.2新式类的多继承

4.2.1 mro序列

MRO是一个有序列表L,在类被创建时就计算出来。
通用计算公式为:

  1. mro(Child(Base1Base2)) = [ Child ] + merge( mro(Base1), mro(Base2), [ Base1, Base2] )(其中Child继承自Base1, Base2

如果继承至一个基类:class B(A)
这时B的mro序列为

  1. mro( B ) = mro( B(A) )
  2. = [B] + merge( mro(A) + [A] )
  3. = [B] + merge( [A] + [A] )
  4. = [B,A]

如果继承至多个基类:class B(A1, A2, A3 …)
这时B的mro序列

  1. mro(B) = mro( B(A1, A2, A3 …) )
  2. = [B] + merge( mro(A1), mro(A2), mro(A3) ..., [A1, A2, A3] )
  3. = ...

计算结果为列表,列表中至少有一个元素即类自己,如上述示例[A1,A2,A3]。merge操作是C3算法的核心。

4.2.2. 表头和表尾
表头:
  列表的第一个元素

表尾:
  列表中表头以外的元素集合(可以为空)

示例
  列表:[A, B, C]
  表头是A,表尾是B和C

4.2.3. 列表之间的+操作
+操作:

[A] + [B] = [A, B]
(以下的计算中默认省略)
---------------------

merge操作示例:

  1. 如计算merge( [E,O], [C,E,F,O], [C] )
  2. 有三个列表
  3. 1 merge不为空,取出第一个列表列表①的表头E,进行判断
  4. 各个列表的表尾分别是[O], [E,F,O],E在这些表尾的集合中,因而跳过当前当前列表
  5. 2 取出列表②的表头C,进行判断
  6. C不在各个列表的集合中,因而将C拿出到merge外,并从所有表头删除
  7. merge( [E,O], [C,E,F,O], [C]) = [C] + merge( [E,O], [E,F,O] )
  8. 3 进行下一次新的merge操作 ......
  9. ---------------------

计算mro(A)方式:

  1. mro(A) = mro( A(B,C) )
  2. 原式= [A] + merge( mro(B),mro(C),[B,C] )
  3. mro(B) = mro( B(D,E) )
  4. = [B] + merge( mro(D), mro(E), [D,E] ) # 多继承
  5. = [B] + merge( [D,O] , [E,O] , [D,E] ) # 单继承mro(D(O))=[D,O]
  6. = [B,D] + merge( [O] , [E,O] , [E] ) # 拿出并删除D
  7. = [B,D,E] + merge([O] , [O])
  8. = [B,D,E,O]
  9. mro(C) = mro( C(E,F) )
  10. = [C] + merge( mro(E), mro(F), [E,F] )
  11. = [C] + merge( [E,O] , [F,O] , [E,F] )
  12. = [C,E] + merge( [O] , [F,O] , [F] ) # 跳过O,拿出并删除
  13. = [C,E,F] + merge([O] , [O])
  14. = [C,E,F,O]
  15. 原式= [A] + merge( [B,D,E,O], [C,E,F,O], [B,C])
  16. = [A,B] + merge( [D,E,O], [C,E,F,O], [C])
  17. = [A,B,D] + merge( [E,O], [C,E,F,O], [C]) # 跳过E
  18. = [A,B,D,C] + merge([E,O], [E,F,O])
  19. = [A,B,D,C,E] + merge([O], [F,O]) # 跳过O
  20. = [A,B,D,C,E,F] + merge([O], [O])
  21. = [A,B,D,C,E,F,O]
  22. ---------------------

结果OK. 那既然python提供了. 为什么我们还要如此⿇烦的计算MRO呢? 因为笔
试.......你在笔试的时候, 是没有电脑的. 所以这个算法要知道. 并且简单的计算要会. 真是项⽬
开发的时候很少有⼈这么去写代码.

这个说完了. 那C3到底怎么看更容易呢? 其实很简单. C3是把我们多个类产⽣的共同继
承留到最后去找. 所以. 我们也可以从图上来看到相关的规律. 这个要大家自己多写多画图就行

百万年薪python之路 -- 面向对象之继承的更多相关文章

  1. 百万年薪python之路 -- 面向对象之三大特性

    1.面向对象之三大特性 1.1封装 封装:就是把一堆代码和数据,放在一个空间,并且可以使用 对于面向对象的封装来说,其实就是使用构造方法将内容封装到 对象 中,然后通过对象直接或者self间接获取被封 ...

  2. 百万年薪python之路 -- 面向对象之:类空间问题以及类之间的关系

    面向对象之:类空间问题以及类之间的关系 1.从空间角度研究类 1.何处添加对象属性 class A: def __init__(self,name): self.name = name def fun ...

  3. 百万年薪python之路 -- 面向对象之 反射,双下方法

    面向对象之 反射,双下方法 1. 反射 计算机科学领域主要是指程序可以访问.检测和修改它本身状态或行为的一种能力(自省) python面向对象中的反射:通过字符串的形式操作对象相关的属性.python ...

  4. 百万年薪python之路 -- 面向对象之所有属性及方法

    1.私有成员公有成员 1.1 类的私有属性 # class A: # # name = '周道镕' # __name = 'zdr' # 私有类的属性 # # def func(self): # pr ...

  5. 百万年薪python之路 -- 面向对象初始

    面向对象初始 1.1 面向过程编程vs函数式编程 函数编程较之面向过程编程最明显的两个特点: 1,减少代码的重用性. 2,增强代码的可读性. 1.2 函数式编程vs面向对象编程 面向对象编程:是一类相 ...

  6. python之路----面向对象的继承特性

    继承 什么是继承 继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类 python中类的继承分为:单继承和多继承 class ...

  7. 百万年薪python之路 -- JS基础介绍及数据类型

    JS代码的引入 方式1: <script> alert('兽人永不为奴!') </script> 方式2:外部文件引入 src属性值为js文件路径 <script src ...

  8. 百万年薪python之路 -- 前端CSS样式

    CSS样式 控制高度和宽度 width宽度 height高度 块级标签能设置高度和宽度,而内联标签不能设置高度和宽度,内联标签的高度宽度由标签内部的内容来决定. 示例: <!DOCTYPE ht ...

  9. 百万年薪python之路 -- 数据库初始

    一. 数据库初始 1. 为什么要有数据库? ​ 先来一个场景: ​ 假设现在你已经是某大型互联网公司的高级程序员,让你写一个火车票购票系统,来hold住十一期间全国的购票需求,你怎么写? 由于在同一时 ...

随机推荐

  1. [VB.NET Tips]字符串转换为日期

    有些字符串需要转换成日期,或者整型转换为日期,可以参考如下思路: Dim result As Date Dim source As String = "20190515" resu ...

  2. Spring Boot (四): Druid 连接池密码加密与监控

    在上一篇文章<Spring Boot (三): ORM 框架 JPA 与连接池 Hikari> 我们介绍了 JPA 与连接池 Hikari 的整合使用,在国内使用比较多的连接池还有一个是阿 ...

  3. Hadoop核心组件之HDFS

    HDFS:分布式文件系统 一句话总结 一个文件先被拆分为多个Block块(会有Block-ID:方便读取数据),以及每个Block是有几个副本的形式存储 1个文件会被拆分成多个Block blocks ...

  4. 看完您如果还不明白 Kerberos 原理,算我输!

    系统环境 操作系统:CentOS 6 或 CentOS 7 JDK 版本:1.8.0_151 Ambari 版本:2.6.1 HDP 版本:2.6.4.0 扩展链接 Kerberos原理--经典对话 ...

  5. Spring boot - 梳理 - 根本上说,Spring Boot项目只不过是一个普通的Spring项目,只是使用了Spring Boot的起步依赖和自动配置

    根本上说,Spring Boot项目只不过是一个普通的Spring项目,只是使用了Spring Boot的起步依赖和自动配置

  6. C语言入门-数组

    今天十月一日,上午看阅兵激情澎湃,但是下午还是要继续写C语言,前面的这块很简单 int number[100]; scanf("%d" , &number[i]); 一.定 ...

  7. springboot 配置mybatis打印sql

    方式 一: ###########################################################配置打印sql############################ ...

  8. vmware上安装centos7虚拟机

    1.1 Linux 的安装 安 装 采 用 在 虚 拟 机 中 安 装 , 以 方 便 不 同 班 级 授 课 时 , 需 要 重 复 安装的情况. 1.1.1 配置虚拟机 1. 在 VMware W ...

  9. [PHP] php, apache, VS Code安装与配置

    1. 下载

  10. Java 学习笔记之 线程isInterrupted方法

    线程isInterrupted方法: isInterrupted()是Thread对象的方法,测试线程是否已经中断. public class ThreadRunMain { public stati ...