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
king1101
V2EX  ›  Python

请教一下关于 Python 的多继承问题,为什么结果差异很大?

  •  
  •   king1101 · 2018-11-13 09:18:18 +08:00 · 2212 次点击
    这是一个创建于 2206 天前的主题,其中的信息可能已经有所发展或是发生改变。

    相关代码 1:

    class A(object):
        def show(self):
            print ('init A...')
    
    class B(A):
        def show(self):
            super(B, self).show()
            print('init B...')
    class C(A):
        def show(self):
            # super(C, self).show()
            print('init C...')
    
    class D(B, C):
        def show(self):
            super(D, self).show()
            print('init D...')
    d = D()
    d.show()
    

    输出的结果是:

    init C...
    init B...
    init D...
    

    这里想问的是为什么没有经过 A,输出 init A...

    相关代码 2:

    class A(object):
        def show(self):
            print ('init A...')
    
    class B(A):
        def show(self):
            super(B, self).show()
            print('init B...')
    class C(A):
        def show(self):
            # super(C, self).show()
            print('init C...')
    
    class D(C, B):    #继承类和代码 1 中的顺序相反
        def show(self):
            super(D, self).show()
            print('init D...')
    d = D()
    d.show()
    

    输出的结果是:

    init C...
    init D...
    

    这里想问的是为什么 B 中的方法没有被调用? 还有的就是新式类的 MRO 算法采用广度优先搜索。在这里是怎么调用的?

    谢谢各位大佬

    12 条回复    2018-11-15 15:00:16 +08:00
    Outliver0
        1
    Outliver0  
       2018-11-13 09:40:16 +08:00
    你可以打印一下__mro__查看顺序
    coroutine
        2
    coroutine  
       2018-11-13 09:51:09 +08:00
    对于, “为什么没有经过 A,输出 init A... ” 你可以参考 Python Cookbook,第 8 章关于 super 的描述。 类 B 里 show 的 super,实际上调用的是类 C 的 show。 而类 C 的 super 已经被你注释掉了。
    Outliver0
        3
    Outliver0  
       2018-11-13 09:52:13 +08:00   ❤️ 2
    第一次的 mro 顺序为
    [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>],
    super(D, self).show()----> super(B, self).show()------>C.show()
    C 已经有 show()方法,所有打印顺序为 init C...,init B...,init D...


    第二次的 mro 顺序为
    [<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>],
    super(D, self).show() ----->C.show()
    C 已经有 show()方法,所有打印顺序为 init C... ,init D...
    coroutine
        4
    coroutine  
       2018-11-13 09:58:48 +08:00   ❤️ 1
    super 的调用顺序实际上,与最终该类的 MRO 的顺序有关。你可以从 D.__mro__ 打印出类 D 的类继承顺序。
    比如您给出的两个例子,的 MRO 分别为
    (__main__.D, __main__.B, __main__.C, __main__.A, object);
    (__main__.D, __main__.C, __main__.B, __main__.A, object)
    如何生成 MRO 顺序,就是 cookbook 里提到的 https://en.wikipedia.org/wiki/C3_linearization 了。
    coroutine
        5
    coroutine  
       2018-11-13 10:01:31 +08:00
    您可以参阅 Python Cookbook Chapter 8: 8.7: Calling a Method on a Parent Class 这一小节。
    liukeai7777
        6
    liukeai7777  
       2018-11-13 10:11:52 +08:00   ❤️ 1
    D 的 mro 是 (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
    init C...
    init B...
    init D...

    所以 super 方法的查找顺序就 DBCA 多以第一个例子 第一步运行到 class D 的 show 方法 ,
    class D show 第一句:
    [super(D, self).show() 查找 B 的 show 方法。运行到 class B 的 show 方法,classB show 执行第一句:super(B, self).show() 然后又找到 class C 的 show 方法 于是打印第一句 init C,执行第二句 init C ]
    class D 的 show 第二句 打印 init C

    同理第二个例子
    MRO 是 DCBA
    king1101
        7
    king1101  
    OP
       2018-11-13 10:12:29 +08:00
    多谢各位大佬,我已经理解了,其实走入了一个误区,认为 super 就是调用父类的方法,其实 super 指的是 MRO 的下一个类,和父类没有实质关联。
    zxcvsh
        8
    zxcvsh  
       2018-11-13 10:13:14 +08:00 via iPhone   ❤️ 1
    百度一下,拓扑排序,好像是这个关键字;图解说明继承检索的顺序很明了
    liukeai7777
        9
    liukeai7777  
       2018-11-13 10:13:21 +08:00
    D 的 mro 是 (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
    init C...
    init B...
    init D...

    所以 super 方法的查找顺序就 DBCA 多以第一个例子 第一步运行到 class D 的 show 方法 ,
    class D show 第一句:
    [super(D, self).show() 查找 B 的 show 方法。运行到 class B 的 show 方法,classB show 执行第一句:super(B, self).show() 然后又找到 class C 的 show 方法 于是打印第一句 init C,执行第二句 init B]
    class D 的 show 第二句 打印 init D

    同理第二个例子
    MRO 是 DCBA
    seven777
        10
    seven777  
       2018-11-13 14:57:56 +08:00
    给楼主一个解药:
    (我不是程序员,但我觉得如此...)
    无论当前的任何编程语言,原理基本想通;
    虽然不同语言有不同的“继承”处理方式,但原理相同。
    统一标准是:
    1,没有什么需求非得复杂的继承来实现,如果有,你设计错了;
    2,如果一个继承关系你搞不明白,不是你技术问题,是设计错了,换思路吧;
    3,“事不过三”在编程中应该是重要思想,比如继承层级不要超过三层,继承源头不要超过三个,再比如 if,while,等判断关系嵌套不要超过三层...
    我瞎说的,但觉得应该如此
    ticotico
        11
    ticotico  
       2018-11-13 15:57:49 +08:00 via Android
    不是把 C 中 super 注释掉了么,D 中的 super 是按 mro 查找 MRO 中下一位的,MRO 在新式类中按的是 bfs 来拍得,最近刚接触 mro 不知道说得对不对,各位大佬轻拍
    RomanCavalry
        12
    RomanCavalry  
       2018-11-15 15:00:16 +08:00
    这个就需要了解 Python MRO 的历史了,参考: http://python.jobbole.com/85685/
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3537 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 04:44 · PVG 12:44 · LAX 20:44 · JFK 23:44
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.