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

简介:

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

图片.png

    需要清楚的一个事情是:

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

        而是python的。


    根据上面的图,编写代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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__' :
 
     =  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类被隐式的调用了两次,只是目前没有察觉到,但是确实存在。

图片.png


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

图片.png

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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__' :
 
     =  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,我不信,所以我尝试了一下,改进后的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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__' :
 
     =  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的威力。然而,不管你信不信,反正我是信了。

    

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

图片.png

看图分析:
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去做

1
2
3
4
5
6
7
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



    改进后的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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__' :
 
     =  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是绝对的保证,在类的继承层次结构中每一个方法只被执行一次



本文转自 TtrToby 51CTO博客,原文链接:http://blog.51cto.com/freshair/2063290

相关文章
|
4月前
|
Python
Python 面向对象编程:解释 Python 中的 `super()` 函数的作用。
Python 面向对象编程:解释 Python 中的 `super()` 函数的作用。
|
6月前
|
C++ Python
Python分享之python super()
Python分享之python super()
|
1月前
|
算法 Python
深入理解Python虚拟机:super超级魔法的背后原理
深入理解Python虚拟机:super超级魔法的背后原理
|
4月前
|
算法 Python
Python 面向对象编程:什么是多重继承,Python 中如何处理多重继承?
Python 面向对象编程:什么是多重继承,Python 中如何处理多重继承?
|
7月前
|
存储 算法 关系型数据库
Python继承及方法解析顺序(MRO)详解 | 示例与super()函数使用
Python继承及方法解析顺序(MRO)详解 | 示例与super()函数使用
151 0
|
8月前
|
Python
Python内置函数--super()
Python内置函数--super()
26 0
|
8月前
|
算法 Python
如何实现Python中的多重继承(Multiple Inheritance)以及方法解析顺序(MRO)
如何实现Python中的多重继承(Multiple Inheritance)以及方法解析顺序(MRO)
210 0
|
10月前
|
算法 C++ Python
python 多态和 super 用法
python 多态和 super 用法
73 0
|
11月前
|
Python
python调用父类方法的三种方式(super调用和父类名调用)
python调用父类方法的三种方式(super调用和父类名调用)
157 0