摘要.
(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 个回答
默认排序
请访问 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 参数,如果要要考虑,也是类似的,加后面就行了
赞同 451106 条评论
分享
收藏喜欢
收起
继续浏览内容
知乎
发现更大的世界
打开
Chrome
继续
成长是一件很私人的事
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 文档
赞同 152 条评论
分享
收藏喜欢
收起
继续浏览内容
知乎
发现更大的世界
打开
Chrome
继续
开发者
36 人赞同了该回答
super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。
总之前人留下的经验就是:保持一致性。要不全部用类名调用父类,要不就全部用 super,不要一半一半。
如果没有复杂的继承结构,super 作用不大。而复杂的继承结构本身就是不良设计。对于多重继承的用法,现在比较推崇 Mixin 的方式,也就是
- 普通类多重继承只能有一个普通父类和若干个 Mixin 类(保持主干单一)
- Mixin 类不能继承普通类(避免钻石继承)
- Mixin 类应该单一职责(参考 Java 的 interface 设计,Mixin 和此极其相似,只不过附带实现而已)
如果按照上述标准,只使用 Mixin 形式的多继承,那么不会有钻石继承带来的重复方法调用,也不会有复杂的查找顺序 —— 此时 super 是可以有无的了,用不用全看个人喜好,只是记得千万别和类名调用的方式混用就好。
赞同 3610 条评论
分享
收藏喜欢
继续浏览内容
知乎
发现更大的世界
打开
Chrome
继续
强答 + 无脑点赞一波流 | 请果取关
如果你把 class 当命名空间用就没事
如果你玩 OOP 继承那一套就有得你蛋痛了。
赞同添加评论
分享
收藏喜欢
继续浏览内容
知乎
发现更大的世界
打开
Chrome
继续
少年先疯队队长
3 人赞同了该回答
- python 实现了多继承, 这个继承的顺序叫 MRO(可以看看 print(class.__mro__))
- super 只是提供了一种调用父类方法. 它并不是实现多继承的!
super 调用父类时, 其实就是搜索: class.__mro__
很多人的疑问其实是 MRO 本身, 这篇文章解释得很好: Python 的方法解析顺序 (MRO) · M-x
赞同 32 条评论
分享
收藏喜欢
继续浏览内容
知乎
发现更大的世界
打开
Chrome
继续
专业搞分布式系统
2 人赞同了该回答
翻译的国外最好的写 super 文章,拿走不写
http://blog.csdn.net/qq_14898613/article/details/53792270
赞同 2添加评论
分享
收藏喜欢
继续浏览内容
知乎
发现更大的世界
打开
Chrome
继续
清华大学 数据科学与信息技术硕士在读
拾人牙慧:
不要一说到 super 就想到父类,super 指的是 MRO (Method Resolution Order,它代表了类继承的顺序) 中的下一个类!
def super(cls, inst):
mro = inst.__class__.mro()
return mro[mro.index(cls) + 1]
它的设计目的:
解决多重继承时父类的查找问题,一般在子类中需要调用父类的方法时,才会这么用。
所以在单重继承中用不用 super 都没关系,
但约定俗成,无论单重继承,还是多重继承,都用 super 。
赞同添加评论
分享
收藏喜欢
继续浏览内容
知乎
发现更大的世界
打开
Chrome
继续
搬砖匠
__mro__及菱形继承问题
赞同添加评论
分享
收藏喜欢
继续浏览内容
知乎
发现更大的世界
打开
Chrome
继续
码农
2 人赞同了该回答
http://blog.csdn.net/johnsonguo/article/details/585193
这里面写的挺好的,可以参考下
赞同 22 条评论
分享
收藏喜欢
继续浏览内容
知乎
发现更大的世界
打开
Chrome
继续
剥极而复参九阳
的解释很到位了,我想说:貌似没法解决参数传递问题啊,只能用 python 所谓的魔法,把参数全部传递过去,可是两个类,作者不同,什么事情都能发生,参数名重复呢,感觉 python 这个机制不怎么样
赞同2 条评论
分享
收藏喜欢
继续浏览内容
知乎
发现更大的世界
打开
Chrome
继续
夫水夫曰
正好第一次接触到这问题,搬运工
inheritance - Understanding Python super() with __init__() methods
赞同添加评论
分享
收藏喜欢
继续浏览内容
知乎
发现更大的世界
打开
Chrome
继续
1 人赞同了该回答
英语要学好。看英语文献很吃力。
赞同 1添加评论
分享
收藏喜欢
继续浏览内容
知乎
发现更大的世界
打开
Chrome
继续
Rogue like 游戏、 旅游 爱好者
2 人赞同了该回答
super(A,self).func 不是把 A 的父类的 func 执行,而是把 A 的父类的类类型序列中的所有类型的 func 都执行一次。
赞同 27 条评论
分享
收藏喜欢
继续浏览内容
知乎
发现更大的世界
打开
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