现在的位置: 首页 > 综合 > 正文

Python super调用父类的非__init__函数

2018年04月30日 ⁄ 综合 ⁄ 共 5685字 ⁄ 字号 评论关闭

先看如下一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class A(object): 
    def onTimer(self): 
        print "A"
                                     
class B(object): 
    def onTimer(self): 
        print "B"
                                     
class C(A, B): 
    def onTimer(self): 
        super(C, self).onTimer() 
        print "C"
                                     
= C() 
c.onTimer()

输出为:

1
2
3
4
''' 
'''

代码是多重继承,希望调到所有的基类的同名函数,但是从输出来看,只有A、C的调用了。

之前总结过 关于Python中的super以及调用父类构造函数[Python],对比上面的代码,在A和B中没有写super,但是也没法super了,因为A的父类是object,是没有onTimer函数的。

现在从Python源码来探究下super:

在工程中搜索super,会在 bltinmodule.c 中找到:

即super是Python的一种内置类型——PySuper_Type。

typeobject.c中PySuper_Type的定义:

其中包含superobject,在typeobject.c中superobject的定义:

其中包含3个指针。

再看PySuper_Type中指定的super_init:

代码中写 super(C, self) 的时候会做super_init操作,结合上面的代码,初步确定type就是前面的class C的类型,obj就是C的对象,obj_type就是C对象的类型。

如果是一个子类对象,那么type和obj_type是一致的,如果是一个父类中的super,那么type就是父类类型,obj_type还是具体子类的类型。

上面得到的super对象在调用onTimer的时候,会有一个super_getattro的逻辑,代码如下:

super_getattro的逻辑是:

先找到starttype,即具体子类的类型,上面就是C的实例c对应的类。

然后遍历starttype的mro,找到当前类型在mro中的位置,在找到的这个位置之后的那些类里面搜索要查找的名字(比如上面的onTimer)。

因此,可以确定super是通过最终子类的mro来确保继承体系上每个类里面的同名函数调用且只被调用一次的。

关于mro,Python文档说明如下:

PyObjectPyTypeObject.tp_mro

Tuple containing the expanded set of base types, starting with the type  itself and ending with object, in Method Resolution Order.

This field is not inherited; it is calculated fresh by PyType_Ready().

了解了上面的一些细节,我们可以输出最开始那段代码各个类的mro来看看:

1
2
3
4
5
6
7
8
9
代码: 
print A.__mro__ 
print B.__mro__ 
print C.__mro__ 
             
结果如下: 
(<class '__main__.A'>, <type 'object'>) 
(<class '__main__.B'>, <type 'object'>) 
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>)

在调用到A的onTimer之后,由于在A的onTimer中没有调用super的onTimer,调用链断掉。

按照上面的规则,我们可以试下这样:

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
代码: 
class A(object): 
    def onTimer(self): 
        print "A"
        super(A, self).onTimer() 
           
class B(object): 
    def onTimer(self): 
        print "B"
           
class C(A, B): 
    def onTimer(self): 
        print "C"
        super(C, self).onTimer() 
           
= C() 
c.onTimer() 
           
print A.__mro__ 
print B.__mro__ 
print C.__mro__ 
           
输出: 
(<class '__main__.A'>, <type 'object'>) 
(<class '__main__.B'>, <type 'object'>) 
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>)

这样,A、B、C都调到了。

但是,A类明显存在问题的:

1
2
3
4
5
6
如果直接使用A类: 
= A() 
a.onTimer() 
          
就会出现错误: 
AttributeError: 'super' object has no attribute 'onTimer'

为了解决这个问题,我们可以增加一个包含onTimer的最终父类,在它里面不再调用父类的onTimer,然后所有需要实现onTimer的子类都存在一个到该最终父类的继承关系。代码如下:

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
48
49
代码: 
class T(object): 
    def onTimer(self): 
        print "T"
      
class A(T): 
    def onTimer(self): 
        print "A"
        super(A, self).onTimer() 
      
class B(T): 
    pass
#   def onTimer(self): 
#       print "B" 
#       super(B, self).onTimer() 
              
class C(T): 
    def onTimer(self): 
        print "C"
        super(C, self).onTimer() 
      
class F(object): 
    pass
      
class G(F, T): 
    def onTimer(self): 
        print "G"
        super(G, self).onTimer() 
      
class D(A, B, C, G): 
    def onTimer(self): 
        print "D"
        super(D, self).onTimer() 
      
class E(D): 
    def onTimer(self): 
        print "E"
        super(E, self).onTimer() 
      
= E() 
e.onTimer() 
      
输出: 
T

另外,朱斌用脚本实现的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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
class MySuperFunc(object): 
    def __init__(self,f,s): 
        super(MySuperFunc,self).__init__() 
        self.f = f 
        self.s = s 
    
    def __call__(self,*params): 
        fp = [self.s] 
        for in params: 
            fp.append(p) 
        self.f(*fp) 
            
class MySuper(object): 
    def __init__(self,curType,obj): 
        super(MySuper,self).__init__() 
        self.curType = curType 
        self.obj = obj 
        
    def __getattr__(self, name): 
        start = False 
        for in self.obj.__class__.__mro__: 
            if not start: 
                if is self.curType: 
                    start = True 
            else
                f = getattr(b, name, None) 
                if is not None and callable(f): 
                    return MySuperFunc(f,self.obj) 
    
class MySuperFunc(object): 
    def __init__(self,f,s): 
        super(MySuperFunc,self).__init__() 
        self.f = f 
        self.s = s 
    
    def __call__(self,*params): 
        fp = [self.s] 
        for in params: 
            fp.append(p) 
        self.f(*fp) 
            
class MySuper(object): 
    def __init__(self,curType,obj): 
        super(MySuper,self).__init__() 
        self.curType = curType 
        self.obj = obj 
        
    def __getattr__(self, name): 
        start = False 
        for in self.obj.__class__.__mro__: 
            if not start: 
                if is self.curType: 
                    start = True 
            else
                f = getattr(b, name, None) 
                if is not None and callable(f): 
                    return MySuperFunc(f,self.obj) 
    
class TimerObj(object): 
    def abc(self): 
        print "T"
    
class D(TimerObj): 
    def abc(self): 
        print "D 1",self 
        MySuper(D,self).abc() 
        print "D 2"
    pass 
    
class A(D): 
    def abc(self): 
        print "A 1",self 
        MySuper(A,self).abc() 
        print "A 2"
    pass 
            
class B(D): 
    def abc(self): 
        print "B 1",self 
        MySuper(B,self).abc() 
        print "B 2"
    
    
class C(A,B): 
    def abc(self): 
        print "C 1",self 
        MySuper(C,self).abc() 
        print "C 2"
    
c = C() 
c.abc()

 

抱歉!评论已关闭.