python

(17 封私信 、 84 条消息) Python中既然可以直接通过父类名调用父类方法为什么还会存在super函数? - 知乎

文章暂存

systemime
2021-02-04
19 min

摘要.

(17 封私信 / 84 条消息) Python 中既然可以直接通过父类名调用父类方法为什么还会存在 super 函数? - 知乎

提问

84

17

# Python 中既然可以直接通过父类名调用父类方法为什么还会存在 super 函数?

关注问题​写回答

[

Python

](//www.zhihu.com/topic/19552832)

# Python 中既然可以直接通过父类名调用父类方法为什么还会存在 super 函数?

比如

class Child(Parent):  
    def __init__(self):  
         Parent.__init__(self) 

这种方式与 super(Child, self).__init__() 有区别么?

关注者

314

被浏览

38,851

关注问题​写回答

​邀请回答

​好问题 2

​1 条评论

​分享

收起 ​

# 13 个回答

默认排序

laike9m

请访问 github.com/evil-huawei

451 人赞同了该回答

针对你的问题,答案是可以,并没有区别。但是这题下的回答我感觉都不够好。

要谈论 super,首先我们应该无视 "super" 这个名字带给我们的干扰。

不要一说到 super 就想到父类!super 指的是 MRO 中的下一个类!
不要一说到 super 就想到父类!super 指的是 MRO 中的下一个类!
不要一说到 super 就想到父类!super 指的是 MRO 中的下一个类!

一说到 super 就想到父类这是初学者很容易犯的一个错误,也是我当年犯的错误。
忘记了这件事之后,再去看这篇文章:Python’s super() considered super!
这是 Raymond Hettinger 写的一篇文章,也是全世界公认的对 super 讲解最透彻的一篇文章,凡是讨论 super 都一定会提到它(当然还有一篇 Python's Super Considered Harmful)。

如果不想看长篇大论就去看这个答案,super 其实干的是这件事:

def super(cls, inst):
    mro = inst.__class__.mro()
    return mro[mro.index(cls) + 1]

两个参数 cls 和 inst 分别做了两件事:
1. inst 负责生成 MRO 的 list
2. 通过 cls 定位当前 MRO 中的 index, 并返回 mro[index + 1]
这两件事才是 super 的实质,一定要记住!
MRO 全称 Method Resolution Order,它代表了类继承的顺序。后面详细说。

举个例子

class Root(object):
    def __init__(self):
        print("this is Root")

class B(Root):
    def __init__(self):
        print("enter B")
        # print(self)  # this will print <__main__.D object at 0x...>
        super(B, self).__init__()
        print("leave B")
        
class C(Root):
    def __init__(self):
        print("enter C")
        super(C, self).__init__()
        print("leave C")
        
class D(B, C):
    pass
        
d = D()
print(d.__class__.__mro__)

输出

enter B
enter C
this is Root
leave C
leave B
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.Root'>, <type 'object'>)

知道了 super 和父类其实没有实质关联之后,我们就不难理解为什么 enter B 下一句是 enter C 而不是 this is Root(如果认为 super 代表 “调用父类的方法”,会想当然的认为下一句应该是 this is Root)。流程如下,在 B 的 __init__ 函数中:

super(B, self).__init__()

首先,我们获取 self.__class__.__mro__,注意这里的 self 是 D 的 instance 而不是 B 的

(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.Root'>, <type 'object'>)

然后,通过 B 来定位 MRO 中的 index,并找到下一个。显然 B 的下一个是 C。于是,我们调用 C 的 __init__,打出 enter C。

顺便说一句为什么 B 的 __init__ 会被调用:因为 D 没有定义 __init__,所以会在 MRO 中找下一个类,去查看它有没有定义 __init__,也就是去调用 B 的 __init__。

其实这一切逻辑还是很清晰的,关键是理解 super 到底做了什么。

于是,MRO 中类的顺序到底是怎么排的呢?**Python’s super() considered super!**中已经有很好的解释,我翻译一下:
在 MRO 中,基类永远出现在派生类后面,如果有多个基类,基类的相对顺序保持不变。

最后的最后,提醒大家.
什么 super 啊,MRO 啊,都是针对 new-style class。如果不是 new-style class,就老老实实用父类的类名去调用函数吧。

--
** 更新,回答

@裘堪将

的问题。**

class A(object) : 
    def __init__(self, strName, *args) :
        self.name = strName
        super(A, self).__init__(*args)

class B(object) : 
    def __init__(self, iID) :
        self.ID= iID

class C(A, B):
    def __init__(self, *args)
        super(C, self).__init__(*args)

没有考虑 keyword 参数,如果要要考虑,也是类似的,加后面就行了

编辑于 2016-01-28

​赞同 451​​106 条评论

​分享

​收藏​喜欢

收起​

继续浏览内容

知乎

发现更大的世界

打开

Chrome

继续

caimaoy

成长是一件很私人的事

15 人赞同了该回答

首先这两种调用是有区别的,在多继承出现的时候,调用是不同的,参考如下代码:

class Base(object):
    def __init__(self):
        print('Base.__init__')


class A(Base):
    def __init__(self):
        print('A.__init__ begin')
        Base.__init__(self)
        print('A.__init__ end')

class B(Base):
    def __init__(self):
        print('B.__init__ begin')
        Base.__init__(self)
        print('B.__init__ end')

class C(A,B):
    def __init__(self):
        print('C.__init__ begin')
        A.__init__(self)
        B.__init__(self)
        print('C.__init__ end')

c = C()
print C.__mro__

运行结果:

C.__init__ begin
A.__init__ begin
Base.__init__
A.__init__ end
B.__init__ begin
Base.__init__
B.__init__ end
C.__init__ end
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <type 'object'>)

使用 super() 的多继承代码

class Base(object):
    def __init__(self):
        print('Base.__init__')

class A(Base):
    def __init__(self):
        print('A.__init__ begin')
        super(A, self).__init__()
        print('A.__init__ end')

class B(Base):
    def __init__(self):
        print('B.__init__ begin')
        super(B, self).__init__()
        print('B.__init__ end')

class C(A,B):
    def __init__(self):
        print('C.__init__ begin')
        super(C, self).__init__()  # Only one call to super() here
        print('C.__init__ end')

c = C()
print C.__mro__

运行结果:

C.__init__ begin
A.__init__ begin
B.__init__ begin
Base.__init__
B.__init__ end
A.__init__ end
C.__init__ end
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <type 'object'>)

当你使用 super() 函数时,Python 会在 MRO 列表上继续搜索下一个类。 只要每个重定义的方法统一使用 super() 并只调用它一次, 那么控制流最终会遍历完整个 MRO 列表,每个方法也只会被调用一次。 这也是为什么在第二个例子中你不会调用两次 Base.__init__() 的原因。

参考:8.7 调用父类方法 - python3-cookbook 2.0.0 文档

发布于 2017-02-03

​赞同 15​​2 条评论

​分享

​收藏​喜欢

收起​

继续浏览内容

知乎

发现更大的世界

打开

Chrome

继续

松鼠奥利奥

开发者

36 人赞同了该回答

super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。

总之前人留下的经验就是:保持一致性。要不全部用类名调用父类,要不就全部用 super,不要一半一半。

如果没有复杂的继承结构,super 作用不大。而复杂的继承结构本身就是不良设计。对于多重继承的用法,现在比较推崇 Mixin 的方式,也就是

  • 普通类多重继承只能有一个普通父类和若干个 Mixin 类(保持主干单一)
  • Mixin 类不能继承普通类(避免钻石继承)
  • Mixin 类应该单一职责(参考 Java 的 interface 设计,Mixin 和此极其相似,只不过附带实现而已)

如果按照上述标准,只使用 Mixin 形式的多继承,那么不会有钻石继承带来的重复方法调用,也不会有复杂的查找顺序 —— 此时 super 是可以有无的了,用不用全看个人喜好,只是记得千万别和类名调用的方式混用就好。

编辑于 2012-01-31

​赞同 36​​10 条评论

​分享

​收藏​喜欢

继续浏览内容

知乎

发现更大的世界

打开

Chrome

继续

est

强答 + 无脑点赞一波流 | 请果取关

如果你把 class 当命名空间用就没事

如果你玩 OOP 继承那一套就有得你蛋痛了。

发布于 2019-12-17

​赞同​​添加评论

​分享

​收藏​喜欢

继续浏览内容

知乎

发现更大的世界

打开

Chrome

继续

ahuigo

少年先疯队队长

3 人赞同了该回答

  1. python 实现了多继承, 这个继承的顺序叫 MRO(可以看看 print(class.__mro__))
  2. super 只是提供了一种调用父类方法. 它并不是实现多继承的!
    super 调用父类时, 其实就是搜索: class.__mro__

很多人的疑问其实是 MRO 本身, 这篇文章解释得很好: Python 的方法解析顺序 (MRO) · M-x

发布于 2016-08-30

​赞同 3​​2 条评论

​分享

​收藏​喜欢

继续浏览内容

知乎

发现更大的世界

打开

Chrome

继续

吉普赛

专业搞分布式系统

2 人赞同了该回答

翻译的国外最好的写 super 文章,拿走不写

http://blog.csdn.net/qq_14898613/article/details/53792270

编辑于 2017-01-03

​赞同 2​​添加评论

​分享

​收藏​喜欢

继续浏览内容

知乎

发现更大的世界

打开

Chrome

继续

用户一鸣

清华大学 数据科学与信息技术硕士在读

拾人牙慧:

不要一说到 super 就想到父类,super 指的是 MRO (Method Resolution Order,它代表了类继承的顺序) 中的下一个类!

def super(cls, inst):
    mro = inst.__class__.mro()
    return mro[mro.index(cls) + 1]

它的设计目的:

解决多重继承时父类的查找问题,一般在子类中需要调用父类的方法时,才会这么用。

所以在单重继承中用不用 super 都没关系,

但约定俗成,无论单重继承,还是多重继承,都用 super 。

发布于 2020-09-23

​赞同​​添加评论

​分享

​收藏​喜欢

继续浏览内容

知乎

发现更大的世界

打开

Chrome

继续

殷浅庸

搬砖匠

__mro__及菱形继承问题

发布于 2020-10-04

​赞同​​添加评论

​分享

​收藏​喜欢

继续浏览内容

知乎

发现更大的世界

打开

Chrome

继续

林智鹏

码农

2 人赞同了该回答

http://blog.csdn.net/johnsonguo/article/details/585193
这里面写的挺好的,可以参考下

发布于 2013-05-08

​赞同 2​​2 条评论

​分享

​收藏​喜欢

继续浏览内容

知乎

发现更大的世界

打开

Chrome

继续

裘堪将

剥极而复参九阳

@laike9m

的解释很到位了,我想说:貌似没法解决参数传递问题啊,只能用 python 所谓的魔法,把参数全部传递过去,可是两个类,作者不同,什么事情都能发生,参数名重复呢,感觉 python 这个机制不怎么样

发布于 2016-01-28

​赞同​​2 条评论

​分享

​收藏​喜欢

继续浏览内容

知乎

发现更大的世界

打开

Chrome

继续

余传

夫水夫曰

正好第一次接触到这问题,搬运工

inheritance - Understanding Python super() with __init__() methods

发布于 2015-03-27

​赞同​​添加评论

​分享

​收藏​喜欢

继续浏览内容

知乎

发现更大的世界

打开

Chrome

继续

ruiming tang

1 人赞同了该回答

英语要学好。看英语文献很吃力。

编辑于 2017-07-12

​赞同 1​​添加评论

​分享

​收藏​喜欢

继续浏览内容

知乎

发现更大的世界

打开

Chrome

继续

布达佩斯小作坊

Rogue like 游戏、 旅游 爱好者

2 人赞同了该回答

super(A,self).func 不是把 A 的父类的 func 执行,而是把 A 的父类的类类型序列中的所有类型的 func 都执行一次。

发布于 2013-12-27

​赞同 2​​7 条评论

​分享

​收藏​喜欢

继续浏览内容

知乎

发现更大的世界

打开

Chrome

继续

​写回答

相关问题

python 中 self 与__init__怎么解释能让小白弄懂? 8 个回答

一个 Pythonic 的类应不应该在 __init__ 中检查参数有效性? 11 个回答

python 应该怎么记各种函数名? 7 个回答

在 python 中有没有什么函数能使另一个指定的函数失去作用? 或有没有什么方法能使某一个我指定的函数失去作用? 10 个回答

Python 里为什么函数可以返回一个函数内部定义的函数? 15 个回答

相关推荐

[

Idle Ideas in 1905

Jerome Klapka Jerome

0 人读过​阅读

](/pub/book/120125079)[

Python 地理空间分析指南(第 2 版)

110 人读过​阅读

](/pub/book/119583521)[

Python 数据分析与应用

黄红梅

113 人读过​阅读

](/pub/book/119582426)

刘看山知乎指南知乎协议知乎隐私保护指引
应用工作申请开通知乎机构号
侵权举报网上有害信息举报专区
京 ICP 证 110745 号
京 ICP 备 13052560 号 - 1
京公网安备 11010802020088 号
互联网药品信息服务资格证书
(京)- 非经营性 - 2017 - 0067 违法和不良信息举报:010-82716601
儿童色情信息举报专区
证照中心
联系我们 © 2021 知乎

想来知乎工作?请发送邮件到 jobs@zhihu.com https://www.zhihu.com/question/20040039

上次编辑于: 5/20/2021, 7:26:49 AM