首页 > 编程语言 >Python从菱形继承到多继承

Python从菱形继承到多继承

时间:2023-10-08 12:01:17浏览次数:43  
标签:__ show 继承 self Python init 菱形 print

目录

一、菱形继承与多继承

Python在继承方面与Java不同,Python可以进行多继承,而Java则不能多继承,替代的是使用的多接口方法。Python继承问题中,最经典的一个问题是菱形继承,然而菱形继承是多继承中的一个特殊的例子。从下图中可以看到他们之间的差异和联系。后面将使用更加普遍的多继承来说明这些继承会遇到的特殊情况。
image

二、多继承可能面临的问题

从前面的介绍可以看到Python多继承存在一些问题如下:

  • 在Class C1中调用的方法,在Class B1和Class B2有同样的实现该如何选择。
  • 多继承的继承顺序问题。
  • 继承过程中有super类名继承这两者有什么问题。
  • 继承过程中初始化的参数该如何申明
  • 继承过程中想通过初始化类进行参数传递如何实现。
  • ...
    为了针对上面出现的若干问题,将对继承进行一些实验来说明上述的问题,对于方法操作介绍的很多,这里对属性的操作进行描述。

三、多继承实现的方法

在Python中多继承可以通过两种方法实现,第一个是类名继承,第二个是super,具体如下:

3.1 类名继承

类名继承的简单使用如下,注意:类名继承的过程中会初始化两次父类,这个问题可以通过super解决。

class A1:
    def __init__(self, a1):
        self.a1 = a1
        self.a_self = 8
        print(a1)


class B1(A1):
    def __init__(self, b1, *args):
        # A1.__init__(self, *args) # super继承全参数传递
        A1.__init__(self, b1)
        self.b_self = 5
        self.a_self = 9
        print(b1)


class C1(A1):
    def __init__(self, c1, *args):
        # A1.__init__(self, *args) # super继承全参数传递
        A1.__init__(self, c1)
        self.c_self = 7
        self.a_self = 10
        print(c1)


class D1(B1, C1):
    def __init__(self, d1, d2):
        C1.__init__(self, d1)
        B1.__init__(self, d2)
        # C1.__init__(self, d1)
        # super(D1, self).__init__(d1, d2) # super继承

    def show(self):
        print(self.a_self)
        print(self.b_self)
        print(self.c_self)


d = D1("1", "2")
# 使用super继承的结果是1,2
# 使用类名继承的结果是1 1 2 2 A1被初始化了两次
# d.show()
print(D1.__mro__)

3.2 super继承

super继承的简单使用如下,值得注意的是在super下的多继承需要使用*args进行参数传递保证初始化不会出现问题,并且可以解决类名继承初始化两次的问题。

class A:
    def __init__(self, a):
        self.a = a
        self.b = 3
        print(a, "A")

    def show(self):
        print(self.a)
        print("A show()")


class B(A):
    def __init__(self, b1, b2, *args):
        print(b1, b2, "B")
        super(B, self).__init__(*args)
        self.b2 = b2
        self.b1 = b1
        self.a = 999

    def show(self):
        print(self.b2, self.b1, self.a)
        print("B show()")


class C(A):
    def __init__(self, c1, c2, *args):
        print(c1, c2, "C")
        super(C, self).__init__(*args)
        self.c2 = c2
        self.c1 = c1
        self.a = 9991

    def show(self):
        print("C show()")


class D(C, B):
    def __init__(self, d1, d2, d3, d4, d5):
        super(D, self).__init__(d1, d2, d3, d4, d5)
        print(d1)
        print(d2)
        self.d1 = d1
        self.d2 = d2
		
		
d = D(2, 3, 4, 5, 6)
print(d)
print(d.a)
d.show()
print(D.__mro__)
# 输出结果
"""
2 3 C
4 5 B
6 A
2
3
<__main__.D object at 0x0000025B243C09C8>
9991
C show()
(<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
"""

3.3 super继承和类名继承的性质

super继承的性质:

  • 按照从左到右的顺序继承,先查询自己有没有方法,没有方法按照从左到右的顺序从父类中查询,最后再去祖类查找。
  • 对于类属性而言,同样的道理,但是属性在过程中可以进行操作,例如在A的中一个属性为10,在B中和C中分别加100,200,在传给D时就变成了310。

对于第一点比较好理解,方法就是有就从自己往上搜索。在属性上有一个点,就是我在A类申明了一个变量,通过类的不断初始化,初始化(B,C)我可以将这个变量传递给D。这个时候就会出现一个情况,执行的结果是从祖类一步步递归下来。参考如下:

class A:
    def __init__(self, a1):
        self.a = a1
        self.self_a = 10
        print("A")

    def show(self):
        print(self, "A show()")


class B(A):
    def __init__(self, b1, b2, *args):
        super(B, self).__init__(*args)
        self.b1 = b1
        self.b2 = b2
        print("B", self.self_a)  # 1210
        self.self_a += 100
        print("B")

    def show(self):
        print(self, "B show()")


class C(A):
    def __init__(self, c1, c2, *args):
        super(C, self).__init__(*args)
        self.c1 = c1
        self.c2 = c2 
        print("C", self.self_a)  # 210
        self.self_a += 1000
        print("C")

    def show(self):
        print(self, "C show()")


class E(A):
    def __init__(self, *args):
        super(E, self).__init__(*args)
        self.self_a += 200
        print(self.show())  # none,因为没有初始化,不会执行到这个方法

    def show(self):
        print("E show()")


class D(B, C, E):
    def __init__(self, d1, d2, *args):
        super(D, self).__init__(*args)
        self.d1 = d1
        self.d2 = d2
        print("D")

    # def show(self):
    #     print(self)
    #     print("D show()")


d = D(1, 2, 3, 4, 5, 6, 7)
d.show()
print(d.self_a)
print(D.__mro__)
"""
A
<__main__.D object at 0x000001FEC09B0BA8> B show()
None
C 210
C
B 1210
B
D
<__main__.D object at 0x000001FEC09B0BA8> B show()
1310
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.E'>, <class '__main__.A'>, <class 'object'>)
"""

对于上述的结果,有人可能会疑惑:不是先初始化B,在初始化C,所以B的结果是10。这是由于B,C中都没有这个变量,因此它会一直按顺序找,再去祖类中找,而这种执行方式是递归调用,由于B、C、E都没有因此去了A,然后在E,C,B的递归调用,因此在B的时候结果是1210。

类名继承的性质:

  • 不用传递全部参数
  • 初始化过程由自己控制

上述有一个问题,就是类名继承是会初始化两次的,因此在继承顺序中,后初始化的结果会把先计算的结果覆盖掉,参考如下:

class A:
    def __init__(self, a1):
        self.a = a1
        self.self_a = 10
        print("A")

    def show(self):
        print(self, "A show()")


class B(A):
    def __init__(self, b1, b2, *args):
        # super(B, self).__init__(*args)
        A.__init__(self, 10)
        self.b1 = b1
        self.b2 = b2
        # print("B", self.self_a)
        print("B")
        self.self_a += 100

    def show(self):
        print(self, "B show()")


class C(A):
    def __init__(self, c1, c2, *args):
        # super(C, self).__init__(*args)
        A.__init__(self, 20)
        self.c1 = c1
        self.c2 = c2
        print("C")
        # self.self_a += 10000

    def show(self):
        print(self, "C show()")


class D(C, B):
    def __init__(self, d1, d2, *args):
        # super(D, self).__init__(*args)
        # C.__init__(self, 3, 5) # 先初始化C,不会覆盖B的累加结果
        # print("C", self.self_a) # self_a的输出结果为110
        B.__init__(self, 1, 2)
        print("B", self.self_a) 
        C.__init__(self, 3, 5)  # 由于重新初始化会覆盖B累加的结果
        print("C", self.self_a) # self_a的输出结果为10
        self.d1 = d1
        self.d2 = d2
        print("D")

    def show(self):
        print(self)
        print("D show()")


d = D(1, 2, 3, 4, 5, 6, 7)
# d.show()
print(D.__mro__)
print(d.self_a)
"""
A
B
B 110
A
C
C 10
D
(<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
10  # 
"""

四、总结

super继承和类名继承的方法,会按照从自己出发逐步往父类走,走到第一个就算结束。对于变量而言,会递归进行,其顺序和类名继承的顺序一致。而Python采取的是C3的继承算法。注:虽然多继承的问题可以通过逐步分析,以及调整代码的顺序解决问题,但是由于实际工程中其过于复杂,尽可能不使用,采取更加优化的解决方法为好,并且通过设计模式和规范进行约束。例如将属性私有化,不允许修改。

参考

博客园-Python的多继承问题-MRO和C3算法

知乎-Python多重继承问题-MRO和C3算法

CSDN-Python多继承与super使用详解

标签:__,show,继承,self,Python,init,菱形,print
From: https://www.cnblogs.com/future-dream/p/17748558.html

相关文章

  • Python wxpython
    wxpythonwx.frame(perent,id,title,pos,size,style,name)——程序窗口控件perent——该控件的父类,一般首个frame没有父类填None。id——常用于Event事件,便于事件获取id值title——程序标题pos——程序出现的位置pos=(x,y)size——整个框架的尺寸size=(x......
  • python queue join task_done的概念及实例解析
    一概念Queue.task_done()在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号Queue.join()实际上意味着等到队列为空,再执行别的操作。 二实例源码一importthreadingimportqueueimporttime#创建队列,用于存储数据q=queue.Qu......
  • python gui开发
    Tkinter(PySimpleGUI)Python标准GUI库,对TCL/TK工具包的一种Python接口封装。Tkinter教程(非常详细)(biancheng.net)打包后比较小,需手写布局,复杂界面功能需自行开发wxPython跨平台GUI库wxWidgets的Python封装。图形用户界面生成器wxFormBuilder(停止维护)Overviewofwx......
  • python内建函数和标准库
    python内建函数Python中的内建函数是指在Python解释器中可以直接调用的函数,不需要导入任何模块或库。这些函数在Python的官方文档中有详细的说明,可直接在Python程序中使用。常用内建函数:abs()返回数字的绝对值。all() 接受一个可迭代对象(如列表)作为参数,如果可迭代对象的所有元......
  • 盘点一个Python自动化办公Excel数据填充实战案例(上篇)
    大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Python自动化办公的问题,一起来看看吧。有个表格,里面每行信息,如下图所示:现在需要将数据贴到另一个文件指定单元格中,另一个文件是这样子的。下图是他的原始数据和他想得到的目标数据,一页有三个,如下所......
  • Python基础环境安装
    环境安装记录,便以后直接一套执行一、准备1.Python下载地址:https://www.python.org/downloads/windows/2.卸载①控制面板寻找Python直接卸载;②查看环境变量path中python的变量,找到对应文件夹,将文件夹删除,后在删除环境变量中有关python的变量;③Win+R运行"regedit"打开注......
  • 深挖 Python 元组 pt.1
    哈喽大家好,我是咸鱼好久不见甚是想念,2023年最后一次法定节假日已经结束了,不知道各位小伙伴是不是跟咸鱼一样今天就开始“搬砖”了呢?我们知道元组(tuple)是Python的内置数据类型,tuple是一个不可变的值序列tuple的元素可以是任何类型,一般用在存储异构数据(例如数据库记录)的场景......
  • JavaSE基础05(方法,重载,调用,类和对象,构造器,封装,继承,方法重写,抽象类,接口,异常)
    面向对象以类的方式组织代码,以对象的组织封装数据;一个Java文件只能有一个public类,必须和文件名一样;java文件里也可以没有public类; 方法的定义方法的使用,修饰符返回值类型方法名(参数类型参数名){方法体return返回值};参数类型包括:基本数据类型和引用数据类......
  • 配置Python国内pip源
    使用按键win+e打开文件管理器,在上方路径栏输入:%APPDATA%查看此目录下是否有pip目录,如果没有则需要创建,并在pip目录下以文本方式添加pip.ini文件。写入内容为[global]index-url=https://pypi.tuna.tsinghua.edu.cn/simple也可以更改index-url的内容为其他pip源。保存退......
  • 【进阶16】Python多线程实战案例
    一、Python实现多线程的几种方式_thread:模块提供了基本的线程和互斥锁支持;更底层的的线程管理实现模块threading:threading模块则通过封装_thread,提供了更加全面的线程使用方法。_thread案例:#*coding:utf-8*#用_thread启动多个线程完成任务import_threadimportthread......