python作为一门动态语言,是和c++一样支持面向对象编程的。相对对象编程有三大特性,分别是继承,封装和多态。今天我们重点讲解的是,python语言中的单继承和多继承。

继承概念:

如果一个类继承了另外一个类时,它将自动获得另一个类的所有属性和方法,那么原有的类称为父类,而新类称为子类。子类继承了其父类的所有属性和方法。同时还可以定义自己的属性和方法。
单继承就是一个子类只能继承一个父类。

格式: class 子类(父类)
举例: class A(B)
A类拥有了B类的所有的特征,A类继承了B类
B类 父类,基类
A类 子类 派生类 后代类

继承的作用:功能的升级和扩展
功能的升级就是对原有 的功能进行完善重新,功能的扩展就是对原本没有的功能进行添加。减少代码的冗余。

下面我们举一个单继承的例子:

class Dog():    #父类def __init__(self):  #父类的属性初始化self.name='狗'self.leg=4def __str__(self):return "名字:%s %d 条腿"%(self.name,self.leg)class Taidi(Dog):  #定义一个Taidi 泰迪 类继承自Dog类 -->单继承passtaidi=Taidi()
print(taidi)    输出结果-->  名字:狗 4 条腿

多继承:

多继承就是一个子类同时继承自多个父类,又称菱形继承、钻石继承。

首先,我们先讲多继承中一个常见方法,单独调用父类的方法。在子类初始化的时候需要手动调用父类的初始化方法进行父类的属性的构造,不然就不能使用提供的属性。

在子类中调用父类的初始化方法格式就是: 父类名._init_(self)

下面举一个单独调用父类方法的例子:

print("******多继承使用类名.__init__ 发生的状态******")
class Parent(object):   #父类def __init__(self, name):print('parent的init开始被调用')self.name = name    #属性的初始化print('parent的init结束被调用')class Son1(Parent):   #单继承 Son1子类继承父类def __init__(self, name, age):print('Son1的init开始被调用')self.age = ageParent.__init__(self, name)        #单独调用父类的属性print('Son1的init结束被调用')class Son2(Parent):  #也是单继承 Son2继承父类def __init__(self, name, gender):print('Son2的init开始被调用')self.gender = gender      #单独调用父类的初始化属性方法Parent.__init__(self, name)print('Son2的init结束被调用')class Grandson(Son1, Son2):      #多继承,继承两个父类def __init__(self, name, age, gender):print('Grandson的init开始被调用')Son1.__init__(self, name, age)  # 单独调用父类的初始化方法Son2.__init__(self, name, gender)print('Grandson的init结束被调用')gs = Grandson('grandson', 18, '男')        #实例化对象
print('姓名:', gs.name)
print('年龄:', gs.age)
print('性别:', gs.gender)print("******多继承使用类名.__init__ 发生的状态******\n\n")

下面让我们看看运行的结果:

******多继承使用类名.__init__ 发生的状态******
Grandson的init开始被调用
Son1的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son1的init结束被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Grandson的init结束被调用
姓名: grandson
年龄: 18
性别: 男
******多继承使用类名.__init__ 发生的状态******

mro顺序

查看上面的运行结果,我们发现由于多继承情况,parent类被的属性被构造了两次,如果在更加复杂的结构下可能更加严重。
为了解决这个问题,Python官方采用了一个算法将复杂结构上所有的类全部都映射到一个线性顺序上,而根据这个顺序就能够保证所有的类都会被构造一次。这个顺序就是MRO顺序。
格式:
类名._mro_()
类名.mro()

多继承中super调用有所父类的被重写的方法
super本质上就是使用MRO这个顺序去调用 当前类在MRO顺序中下一个类。 super().init()则调用了下一个类的初始化方法进行构造。

print("******多继承使用super().__init__ 发生的状态******")
class Parent(object):def __init__(self, name, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数print('parent的init开始被调用')self.name = nameprint('parent的init结束被调用')class Son1(Parent):def __init__(self, name, age, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数print('Son1的init开始被调用')self.age = agesuper().__init__(name, *args, **kwargs)  # 为避免多继承报错,使用不定长参数,接受参数print('Son1的init结束被调用')class Son2(Parent):def __init__(self, name, gender, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数print('Son2的init开始被调用')self.gender = gendersuper().__init__(name, *args, **kwargs)  # 为避免多继承报错,使用不定长参数,接受参数print('Son2的init结束被调用')class Grandson(Son1, Son2):def __init__(self, name, age, gender):print('Grandson的init开始被调用')# 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍# 而super只用一句话,执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因# super(Grandson, self).__init__(name, age, gender)super().__init__(name, age, gender)print('Grandson的init结束被调用')print(Grandson.__mro__)gs = Grandson('grandson', 18, '男')
print('姓名:', gs.name)
print('年龄:', gs.age)
print('性别:', gs.gender)
print("******多继承使用super().__init__ 发生的状态******\n\n")

查看下运行结果:

******多继承使用super().__init__ 发生的状态******
(<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)
Grandson的init开始被调用
Son1的init开始被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Son1的init结束被调用
Grandson的init结束被调用
姓名: grandson
年龄: 18
性别: 男
******多继承使用super().__init__ 发生的状态******

单继承中super

print("******单继承使用super().__init__ 发生的状态******")
class Parent(object):def __init__(self, name):print('parent的init开始被调用')self.name = nameprint('parent的init结束被调用')class Son1(Parent):def __init__(self, name, age):print('Son1的init开始被调用')self.age = agesuper().__init__(name)  # 单继承不能提供全部参数print('Son1的init结束被调用')class Grandson(Son1):def __init__(self, name, age, gender):print('Grandson的init开始被调用')super().__init__(name, age)  # 单继承不能提供全部参数print('Grandson的init结束被调用')gs = Grandson('grandson', 12, '男')
print('姓名:', gs.name)
print('年龄:', gs.age)
#print('性别:', gs.gender)
print("******单继承使用super().__init__ 发生的状态******\n\n")

运行结果:

******单继承使用super().__init__ 发生的状态******
Grandson的init开始被调用
Son1的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son1的init结束被调用
Grandson的init结束被调用
姓名: grandson
年龄: 12
******单继承使用super().__init__ 发生的状态******

下面让我们总结下:

  • MRO保证了多继承情况 每个类只出现一次
  • super().__init__相对于类名.init,在单继承上用法基本无差
  • 但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次
  • 多继承时,使用super方法,对父类的传参数,应该是由于python中super的算法导致的原因,必须把参数全部传递,否则会报错
  • 单继承时,使用super方法,则不能全部传递,只能传父类方法所需的参数,否则会报错
  • 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍,而使用super方法,只需写一句话便执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因

下面是一个简答的面试题:

class Parent(object):x = 1class Child1(Parent):passclass Child2(Parent):passprint(Parent.x, Child1.x, Child2.x)
Child1.x = 2
print(Parent.x, Child1.x, Child2.x)
Parent.x = 3
print(Parent.x, Child1.x, Child2.x)

运行结果:

1 1 1
1 2 1
3 2 3

python中的单继承,多继承和mro顺序相关推荐

  1. 在Python中创建单例

    这个问题不是为了讨论是否需要单例设计模式 ,是否是反模式,还是针对任何宗教战争,而是要讨论如何以最pythonic的方式在Python中最好地实现此模式. 在这种情况下,我定义"最pytho ...

  2. Python中的单引号,双引号,三重引号的区别

    经常有人问Python中的单引号,双引号,三重引号的区别,其实上还是有区别的. 单引号和双引号基本上没什么区别,区别只在于下面这种情况. >>> S1 = 'I do like &q ...

  3. 单例模式(Python中的单例类)

    目录 一.单例模式的概述 二.单例模式的优缺点 三.在Python中,单例模式有以下几种实现方式 1.通过魔法方法__new__实现 2.通过模块的导入 3.通过装饰器实现 4.通过使用类实现 一.单 ...

  4. python中的单引号,双引号,三引号,引号三连鞭!!!

    python中有单引号,双引号,还有三引号,本文将介绍三种引号的具体用法. python中,单引号和双引号都可以代表字符串,如下所示: a = 'hello world' print(a) b = & ...

  5. python中的单下划线和双下划线_python中的单下划线和双下划线

    1. _ 的含义在python的类中,没有真正的私有化,不管是方法还是属性,为了编程的需要,约定加了下划线 _ 的属性和方法不属于API,不应该在类的外面访问,也不会被from M import * ...

  6. python中的单下划线和双下划线_python 里面的单下划线与双下划线的区别(私有和保护)...

    Python 用下划线作为变量前缀和后缀指定特殊变量. _xxx 不能用'from moduleimport *'导入 __xxx__ 系统定义名字 __xxx 类中的私有变量名 核心风格:避免用下划 ...

  7. python中的单例设计模式

    单例 -- 让 类 创建的对象,在系统中 只有 唯一的一个实例 定义一个 类属性,初始值是 None,用于记录 单例对象的引用 重写 __new__ 方法 如果 类属性 is None,调用父类方法分 ...

  8. python reduce函数怎么用的_我如何仅通过使用reduce函数在python中创建单...

    使用reduce似乎很难实现,因为如果您" reduce"的两个元组都没有相同的字母,那么您将无法计算结果.如何减少('a',1)和('b',1)到可行的结果? 我能做的最好是l ...

  9. Python中变量的作用域?(变量查找顺序)

    函数作用域的LEGB顺序 1.什么是LEGB? L: local 函数内部作用域 E: enclosing 函数内部与内嵌函数之间 G: global 全局作用域 B: build-in 内置作用 p ...

最新文章

  1. Oracle 12C -- 清空audit记录
  2. Linux磁盘扇区和内存页,技术|检查linux中硬盘损坏的扇区和区块
  3. HDFS源码分析心跳汇报之数据结构初始化
  4. 浅谈Struts2和Sturts1的区别
  5. flash builder 关联svn
  6. 小康陪你学JAVA--------三大循环之Do-while循环
  7. iOS 时间戳的转换
  8. ras的c语言源代码文档,µMore(µITRON操作系统)--功能概况
  9. python 参数解析器_Python中最好用的命令行参数解析工具
  10. 那些年我们清除过的浮动
  11. H5 获取连接参数,判断是否是手机端
  12. win 7 连接打印机
  13. 速来围观:飞时达土石方计算软件FastTFT V15.2正式升级发布
  14. matlab生成的fig文件名字修改
  15. 服务器防火墙开启导致无法远程连接解决方案
  16. 2019年终总结,一朝看尽长安花
  17. Jsp实现注册登录以及忘记找回密码等操作(上)
  18. CornerNet,CenterNet关键代码解读: kp,_decode,left pooling
  19. dbm和发射功率得对照表
  20. 非机动车检测数据集(用于训练目标跟踪)

热门文章

  1. 设计模式之_动态代理_02
  2. Bootstrap组件_警告框
  3. Spring Cloud Gateway 源码解析(4)-- filter
  4. mysql 中eq_表达式中的运算符EQ NE GT GE LT LE…..
  5. STM32产品名称命名规则
  6. timeit统计运行时间
  7. Python学习手册之Python介绍、基本语法(二)
  8. 23种设计模式之《单例模式》
  9. 上海网域CEO肖确伟:IDC精细化运营探讨
  10. MySQL空密码用户清理