V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
ChangHaoWei
V2EX  ›  Python

python3.6.1 的一个很好玩的问题

  •  1
     
  •   ChangHaoWei · 2017-05-18 11:49:18 +08:00 · 3278 次点击
    这是一个创建于 2780 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近遇到到一个问题,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

    21 条回复    2017-05-19 16:13:43 +08:00
    Cooky
        1
    Cooky  
       2017-05-18 12:01:01 +08:00 via Android
    你觉得你模棱两可的调用,Python 会知道调哪个?
    ChangHaoWei
        2
    ChangHaoWei  
    OP
       2017-05-18 12:02:32 +08:00
    @Cooky python2 貌似没问题啊
    hareandlion
        3
    hareandlion  
       2017-05-18 12:06:39 +08:00 via iPhone
    按声明时的父类顺序的
    ChangHaoWei
        4
    ChangHaoWei  
    OP
       2017-05-18 12:07:54 +08:00
    @hareandlion 你可以自己贴代码去试试,我反正结果是我主题上说的,忽略了第二个超类
    ChangHaoWei
        5
    ChangHaoWei  
    OP
       2017-05-18 12:09:06 +08:00
    更新
    ```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
    ```
    XYxe
        6
    XYxe  
       2017-05-18 12:10:15 +08:00
    这应该是 MRO 的问题,2.7.13 和 3.5.3 的结果和你的是一样的。
    hareandlion
        7
    hareandlion  
       2017-05-18 12:10:31 +08:00
    ....这是 Python 的 MRO 机制决定的,Python3 用的是 C3 method,看看官方文档吧
    ChangHaoWei
        8
    ChangHaoWei  
    OP
       2017-05-18 12:11:40 +08:00
    @XYxe 问题是,为什么会这样,我后面贴出的代码本质上差不多,为什么又能正常的调用所有超类的初始化方法呢?
    ChangHaoWei
        9
    ChangHaoWei  
    OP
       2017-05-18 12:12:34 +08:00
    @hareandlion 方便的话,麻烦你指点以下。
    XYxe
        10
    XYxe  
       2017-05-18 12:14:30 +08:00
    @ChangHaoWei #8 两个代码是有区别的。
    super 就是查找 mro 的下一个类。C 的 mro:[C, A, B, object],所以你新的代码就是按照这个顺序来调用__init__的。
    Cooky
        11
    Cooky  
       2017-05-18 12:40:07 +08:00 via Android
    @ChangHaoWei A B 里面也要有 super (
    AnyISalIn
        12
    AnyISalIn  
       2017-05-18 13:04:02 +08:00
    mro
    CallMeHoney
        13
    CallMeHoney  
       2017-05-18 13:54:43 +08:00
    MRO C3 算法,简单来说就是深度优先遍历
    XYxe
        14
    XYxe  
       2017-05-18 14:03:41 +08:00
    @CallMeHoney #13 不是深度优先遍历。
    zhengxiaowai
        15
    zhengxiaowai  
       2017-05-18 14:12:15 +08:00
    super 并不是调用父类,而是调用 MRO 中的,具体要看 MRO 中顺序
    Readme16
        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()
    ```
    jiang42
        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

    用手机打的字,有点乱……
    jiang42
        18
    jiang42  
       2017-05-18 14:29:11 +08:00 via iPhone
    也和 MRO 有一丢丢关系啦,但是不是楼上所说的锅全丢给 MRO
    hugo775128583
        19
    hugo775128583  
       2017-05-18 14:42:34 +08:00
    楼上已经说了 MRO 没毛病
    weyou
        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()
    ChangHaoWei
        21
    ChangHaoWei  
    OP
       2017-05-19 16:13:43 +08:00
    @weyou 谢谢。幸苦你帮我解决这个问题了。我最近遇到一个 pyqt5 的一个问题,能不能帮我解决?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1284 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 17:47 · PVG 01:47 · LAX 09:47 · JFK 12:47
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.