Python多重继承引发的问题——牛逼的super

少说废话多做事先上一个图,此图用win7下的画图工具所画,当然,这不是重点

需要清楚的一个事情是:

任何类,都默认并隐式的继承object类(根类),在上面的图中,Transformers类同时继承了Car和Ship类。那么Car和Ship又隐式的继承了object类,注意,这个object类不是我自己定义的

而是python的。

根据上面的图,编写代码

class Car:
    #描述汽车
    call_car_record = 0 #记录调用次数
    def __init__(self, car_name):
        self.car_name = car_name

    def land_run(self):
        print("{0} Run on land".format(self.car_name))
        self.call_car_record += 1

class Ship:
    #描述轮船
    call_ship_record = 0 #记录调用次数
    def __init__(self, ship_name):
        self.ship_name = ship_name

    def running_in_sea(self):
        print("{0} Running in the sea".format(self.ship_name))
        self.call_ship_record += 1

class Transformers(Car, Ship):
    #变形金刚
    call_tran_record = 0 #记录调用次数
    def __init__(self, car_name, ship_name, tran_name):
        Car.__init__(self, car_name)
        Ship.__init__(self, ship_name)
        self.tran_name = tran_name

    def transfiguration(self):
        print("{0} start Transfiguration...".format(self.tran_name))
        self.call_tran_record += 1

if __name__ == '__main__':

    t = Transformers("byd", "gz01", "toby")
    t.land_run()
    t.running_in_sea()
    t.transfiguration()
    print(t.call_tran_record, t.call_car_record, t.call_ship_record)

"""
输出的结果:
    byd Run on land
    gz01 Running in the sea
    toby start Transfiguration...
    1 1 1
"""

那么,上面的代码,面临的问题正是我在上图中的描述,也就是说object类被隐式的调用了两次,只是目前没有察觉到,但是确实存在。

OK!!!我想证明这个问题,证实它是不是被调用两次。我再上一个图,如下:

上面的图,就是传说中的砖石继承,啥是砖石继承?只是这个类的继承视图看起来和砖石一样,当然这只是我的理解。

如果用这个图来证明BaseClass会不会被调用2次,那么需要转化成代码。代码如下:

class BaseClass:
    num_base_calls = 0 #记录基类被调用次数
    def work(self):
        print("work method of calling base class")
        self.num_base_calls += 1

class AClass(BaseClass):
    num_aclass_calls = 0 #记录被调用次数
    def work(self):
        BaseClass.work(self)
        print("work method of calling AClass class")
        self.num_aclass_calls += 1

class BClass(BaseClass):
    num_bclass_calls = 0 #记录被调用次数
    def work(self):
        BaseClass.work(self)
        print("work method of calling BClass class")
        self.num_bclass_calls += 1

class CClass(AClass, BClass):
    num_cclass_calls = 0 #记录被调用次数
    def work(self):
        AClass.work(self)
        BClass.work(self)
        print("Calling work method on CClass")
        self.num_cclass_calls += 1

if __name__ == '__main__':

    c = CClass()
    c.work()
    print(c.num_cclass_calls, c.num_aclass_calls, c.num_bclass_calls, c.num_base_calls)

"""
结果输出:
work method of calling base class
work method of calling AClass class
work method of calling base class
work method of calling BClass class
Calling work method on CClass
1 1 1 2
"""

从输出的结果来看,BaseClass真的被调用了两次,这个基类是我自己定义的,这不是隐式调用,是明目张胆的在调用啊!!!

如何解决这个问题?专业人士说用super,我不信,所以我尝试了一下,改进后的代码如下:

class BaseClass:
    num_base_calls = 0 #记录基类被调用次数
    def work(self):
        print("work method of calling base class")
        self.num_base_calls += 1

class AClass(BaseClass):
    num_aclass_calls = 0 #记录被调用次数
    def work(self):
        super().work()
        print("work method of calling AClass class")
        self.num_aclass_calls += 1

class BClass(BaseClass):
    num_bclass_calls = 0 #记录被调用次数
    def work(self):
        super().work()
        print("work method of calling BClass class")
        self.num_bclass_calls += 1

class CClass(AClass, BClass):
    num_cclass_calls = 0 #记录被调用次数
    def work(self):
        super().work()
        print("Calling work method on CClass")
        self.num_cclass_calls += 1
if __name__ == '__main__':

    c = CClass()
    c.work()
    print(c.num_cclass_calls, c.num_aclass_calls, c.num_bclass_calls, c.num_base_calls)
"""
输出结果:
work method of calling base class
work method of calling BClass class
work method of calling AClass class
Calling work method on CClass
1 1 1 1
"""

事实证明,BaseClass这个基类真的只被调用了一次,这就是super的威力。然而,不管你信不信,反正我是信了。

那分析一下他的调用顺序,我又画了个图:

看图分析:1、CClass类的work()方法调用了super.work,其实是引用了AClass.work()2、在AClass.work()中,调用了super.work(),这时候是引用了BClass.work(),而不是BaseClass.work(),这就是所谓的下一个方法3、接着在BClass.work()中调用了super.work()方法,这时候才是去调用BaseClass.work()方法

现在回到第一个多重继承的例子,在多重继承的例子中,Transformers类的__init__方法中,为了实现能调用两个父类的__init__()方法

我最初的做法是,分别写了两次调用。这里面临的问题是,有两个父类的初始化方法要调用,而且还是需要不同的参数。

那么,我一开始就写成如下面代码这样,不是不行,而是不是我追求的目标,我是一个追求完美的人,所以我决定改进它,也是用super去做

class Transformers(Car, Ship):
    #变形金刚
    call_tran_record = 0
    def __init__(self, car_name, ship_name, tran_name):
        Car.__init__(self, car_name) #调用Car类的初始化方法
        Ship.__init__(self, ship_name) #调用Ship类的初始化方法
        self.tran_name = tran_name

改进后的代码如下:

class Car:
    #描述汽车
    call_car_record = 0 #记录调用次数
    def __init__(self, car_name=None, **kwargs):
        super().__init__(**kwargs)
        self.car_name = car_name

    def land_run(self):
        print("{0} Run on land".format(self.car_name))
        self.call_car_record += 1

class Ship:
    #描述轮船
    call_ship_record = 0 #记录调用次数
    def __init__(self, ship_name=None, **kwargs):
        super().__init__(**kwargs)
        self.ship_name = ship_name

    def running_in_sea(self):
        print("{0} Running in the sea".format(self.ship_name))
        self.call_ship_record += 1

class Transformers(Car, Ship):
    #变形金刚
    call_tran_record = 0 #记录调用次数
    def __init__(self, tran_name=None, **kwargs):
        super().__init__(**kwargs)
        self.tran_name = tran_name

    def transfiguration(self):
        print("{0} start Transfiguration...".format(self.tran_name))
        self.call_tran_record += 1

if __name__ == '__main__':

    t = Transformers(tran_name="toby", car_name="byd", ship_name="qq")
    t.land_run()
    t.running_in_sea()
    t.transfiguration()
    print(t.call_tran_record, t.call_car_record, t.call_ship_record)

关于如何分析它的执行顺序?请用“下一个方法”的思路去理解它。

最后,再总结一下super的牛逼之处

1、在类的继承层次结构中,只想调用"下一个方法",而不是父类的方法

2、super的目标就是解决复杂的多重继承问题(基类被调用两次的问题)

3、super是绝对的保证,在类的继承层次结构中每一个方法只被执行一次

原文地址:http://blog.51cto.com/freshair/2063290

时间: 2024-11-10 17:26:54

Python多重继承引发的问题——牛逼的super的相关文章

Python——集合是一个非常之牛逼的数据比较方式

什么是集合?对似懂非懂或者被集合搞晕了的同学可以来看一下.    首先不想说太多概念性的东西,免得又晕了! 请看下面我写的例子,看完之后自然就知道啥是集合以及如何去应用它 #变量a是一个集合类型 a = set() print(type(a)) #定义一个列表,并存入很多重复的数字 dt = [12,9,2,8,4,9,2,7,4,5,6,11,5,7,5,7,9,8,7,2] for i in dt: #遍历列表     a.add(i) #将列表中的每一个元素添加进集合 #打印集合,发现,自

最牛逼的开源机器学习框架,你知道几个

最牛逼的开源机器学习框架,你知道几个 机器学习毫无疑问是当今最热的话题,它已经渗透到生活的方方面面,在移动互联网中混不懂点机器学习都不好意思,说几个能看的到的,经常用邮箱吧,是不是感觉垃圾邮件比N年前变少了,无聊了和siri聊过天不,想坐一下无人驾驶汽车吗,手累了用脸解个锁,智能化产品推荐是不是让你更懒了.看不到的就更多了:信用卡欺诈监测保证你的交易安全,股票交易/量化投资(知道你的高收益理财怎么来的吗?),手势识别(用过海豚浏览器的手势吗),还有医学分析等等,巨头们为了在未来占领先机,前仆后继

如何成为牛逼的程序员

著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处.作者:vczh链接:http://zhuanlan.zhihu.com/p/19796639来源:知乎 我有一个想法,不一定对. 第一篇文章(第一篇文章就贡献给投票了 - vczh的日常 - 知乎专栏)果然给了我灵感耶,标题的图片就是从评论里截出来的.为了以后回答那些层出不穷的月经问题,我决定写下这么一篇文章,讲一下我对牛逼的程序员的理解.为什么我要讲这个呢,当然首先我还是觉得自己是很牛逼的,不然我就不会讲这个了(误 一个牛逼的

Java 11 正式发布,这 8 个逆天新特性教你写出更牛逼的代码

美国时间 09 月 25 日,Oralce 正式发布了 Java 11,这是据 Java 8 以后支持的首个长期版本. 为什么说是长期版本,看下面的官方发布的支持路线图表. Java 11 正式发布,这 8 个逆天新特性教你写出更牛逼的代码可以看出 Java 8 扩展支持到 2025 年,而 Java 11 扩展支持到 2026 年. 现在大部分都在用 Java 8,Java 9 和 10 目前很少有人在用,至少我没有发现有公司在生产环境应用的,那就是找死. 现在 Java 11 长期支持,也已

二十岁出头,你一无所有,但你却拥有一切,因为你还有牛逼的梦想。 可那又怎样,只有行动,才能解除你所有的不安

我从来没有看到过一句话,如此让我共鸣.二十岁出头,你一无所有,但你却拥有一切,因为你还有牛逼的梦想. 可那又怎样,只有行动,才能解除你所有的不安 (一)嘴上说说的人生 那年我在离家的时候一个劲地往自己的硬盘里塞<灌篮高手>,我妈一副嗤之以鼻的表情 看着我,似乎是在说:"这么大的人了居然还这么喜欢看动漫." 我不知道怎么回应她,只好耸耸肩,因为我实在无法对我亲爱的娘亲说明这部动漫对我的 意义. 你知道,有些歌有些东西就是有那种力量.哪怕它在你的手机里藏了好几年,哪怕它早就 过

如何成为一位牛逼的大咖

每个人都想过做一件牛逼的事情,做一个牛逼的项目,或者想成为一个牛逼的大咖.甚至我们还不懂到底什么是大咖,所谓的"大咖"其实就是在某个领域有这一定影响力,拥有众多的粉丝和号召力的人.如果想成为一位牛逼的大咖,那么你必须在某个领域有着自己的独有的价值.做大咖很多人以为会很舒服,人只要出了名什么好事都会轮到他.不但会获得很多的社会资源还有很多的商业价值.所以,能成为一位牛逼的大咖说明已经非常成功了,就是不操作什么项目也活得相当滋润了.很多人把我当成了大咖来向我讨教经验,其实我也不是什么大咖,

为什么我会认为SAP是世界上最好用最牛逼的ERP系统,没有之一?

为什么我认为SAP是世界上最好用最牛逼的ERP系统,没有之一?玩过QAD.Tiptop.用友等产品,深深觉得SAP是贵的有道理! 一套好的ERP系统,不仅能够最大程度承接适配企业的管理和业务流程,在技术上面也能够做到快速部署和挑战.而对用户而言,好用且逻辑性强,体验好.便利可掌控才是能够抓住用户的需求点.很可惜的是,纵观世界上这么多的ERP系统,真正能够为用户考虑而且有自己的核心竞争力的并不多. 我认为一套好的ERP系统,不仅仅是一套软件,更是一个管理思想.选型ERP,要从以下几方面考虑: 一.

比较大小的牛逼代码

#include<iostream> using namespace std; int main(){ int x1,y1,x2,y2; while(cin>>x1>>y1>>x2>>y2){ if( x1> x2 ) x1^= x2^= x1^= x2; if( y1> y2 ) y1^= y2^= y1^= y2; } return 0; } 这段代码太牛逼了,自己现在看不懂,但是,真的好省力!!

Unity牛逼的2D纹理功能

[Unity牛逼的2D纹理功能] 1.可直接将贴图生成成为Cubemap. 2.自动生成Mipmap. 3.查看纹理被当前场景哪些对象引用.在Project窗口中,右击图像,选择 参考:file:///C:/Program%20Files%20(x86)/Unity/Editor/Data/Documentation/html/en/Manual/class-TextureImporter.html