最近遇到到一个问题,super 在多继承的类中只调用第一个类的函数。
代码如下
class A(object):
def __init__(self, **kwargs):
print('a1')
print('a', kwargs)
print('a2')
class B(object):
def __init__(self, **kwargs):
print('b1')
print('b', kwargs)
print('b2')
class C(A, B):
def __init__(self, **kwargs):
print('c1')
super(C, self).__init__()
print('c2')
if __name__ == '__main__':
c = C()
结果是
c1
a1
a {}
a2
c2
这就很尴尬了,为什么不会出发 B().init 呢
1
Cooky 2017-05-18 12:01:01 +08:00 via Android
你觉得你模棱两可的调用,Python 会知道调哪个?
|
2
ChangHaoWei OP @Cooky python2 貌似没问题啊
|
3
hareandlion 2017-05-18 12:06:39 +08:00 via iPhone
按声明时的父类顺序的
|
4
ChangHaoWei OP @hareandlion 你可以自己贴代码去试试,我反正结果是我主题上说的,忽略了第二个超类
|
5
ChangHaoWei OP 更新
```python class A(): def __init__(self, **kwargs): super().__init__() print('a1') print('a', kwargs) print('a2') class B(): def __init__(self, **kwargs): super().__init__() print('b1') print('b', kwargs) print('b2') class C(A, B): def __init__(self, **kwargs): print('c1') super().__init__() print('c2') if __name__ == '__main__': c = C() ``` 这样的代码就能有下面的输出。。为什么,求有相关研究的高手给我一个答案 ``` c1 b1 b {} b2 a1 a {} a2 c2 ``` |
6
XYxe 2017-05-18 12:10:15 +08:00
这应该是 MRO 的问题,2.7.13 和 3.5.3 的结果和你的是一样的。
|
7
hareandlion 2017-05-18 12:10:31 +08:00
....这是 Python 的 MRO 机制决定的,Python3 用的是 C3 method,看看官方文档吧
|
8
ChangHaoWei OP @XYxe 问题是,为什么会这样,我后面贴出的代码本质上差不多,为什么又能正常的调用所有超类的初始化方法呢?
|
9
ChangHaoWei OP @hareandlion 方便的话,麻烦你指点以下。
|
10
XYxe 2017-05-18 12:14:30 +08:00
@ChangHaoWei #8 两个代码是有区别的。
super 就是查找 mro 的下一个类。C 的 mro:[C, A, B, object],所以你新的代码就是按照这个顺序来调用__init__的。 |
11
Cooky 2017-05-18 12:40:07 +08:00 via Android
@ChangHaoWei A B 里面也要有 super (
|
12
AnyISalIn 2017-05-18 13:04:02 +08:00
mro
|
13
CallMeHoney 2017-05-18 13:54:43 +08:00
MRO C3 算法,简单来说就是深度优先遍历
|
14
XYxe 2017-05-18 14:03:41 +08:00
@CallMeHoney #13 不是深度优先遍历。
|
15
zhengxiaowai 2017-05-18 14:12:15 +08:00
super 并不是调用父类,而是调用 MRO 中的,具体要看 MRO 中顺序
|
16
Readme16 2017-05-18 14:19:55 +08:00
```python
class A(object): def __init__(self, **kwargs): super().__init__() print('a1') print('a', kwargs) print('a2') class B(object): def __init__(self, **kwargs): print('b1') print('b', kwargs) print('b2') class C(A, B): def __init__(self, **kwargs): print('c1') super().__init__() print('c2') if __name__ == '__main__': c = C() ``` |
17
jiang42 2017-05-18 14:28:13 +08:00 via iPhone
1. 和 MRO 没关系,楼主说的是 B.__init__未调用
2. super 返回的是一个 delegate class,会去查找父类或者 sibling class,你这个情况在 AC 里用 super 就够了。原始的调用顺序 C 调了 super 找到 A,但是 A 没有调 super,所以没有找到它的 sibling B。 所以 best practice 是所有都用 super 用手机打的字,有点乱…… |
18
jiang42 2017-05-18 14:29:11 +08:00 via iPhone
也和 MRO 有一丢丢关系啦,但是不是楼上所说的锅全丢给 MRO
|
19
hugo775128583 2017-05-18 14:42:34 +08:00
楼上已经说了 MRO 没毛病
|
20
weyou 2017-05-18 19:09:40 +08:00 3
@zhengxiaowai
@jiang42 同意两位的意见。 Python 的 super()帮助文档引用了一个链接: http://rhettinger.wordpress.com/2011/05/26/super-considered-super/ 这个文章的结论是 super().method()要能够 work,所有在祖先树上的 class 必须以合作模式来设计,这就是: 1. 被 super()调用的 method()必须存在 2. 调用者和被调用者必须要具有相同的参数(签名) 3. 每个出现的 method()必须也使用了 super() 我加了一些注释,我想这样就比较清楚了。 class A(object): ....def __init__(self, **kwargs): ........super().__init__() # 在 MRO(A,B,object)里面找到了下一级是 B. 调用 B.__init__(这句如果没有 B.__init__就调用不到) ........print('a1') ........print('a', kwargs) ........print('a2') class B(object): ....def __init__(self, **kwargs): ........super().__init__() # 在 MRO(B,object )里面找到了下一级是 object. 调用 object.__init__() ........print('b1') ........print('b', kwargs) ........print('b2') class C(A, B): ....def __init__(self, **kwargs): ........print('c1') ........super().__init__() # 在 MRO(C,A,B,object )里面找到了下一级是 A. . 调用 A.__init__() ........print('c2') if __name__ == '__main__': ....print(C.__mro__) # MRO: (C,A,B,object) ....c = C() |
21
ChangHaoWei OP @weyou 谢谢。幸苦你帮我解决这个问题了。我最近遇到一个 pyqt5 的一个问题,能不能帮我解决?
|