我想了解
super()
的用法。从外观上看,两个子类都可以创建,就很好。 我很想知道以下两个子类之间的实际区别。
class Base(object):
def __init__(self):
print "Base created"
class ChildA(Base):
def __init__(self):
Base.__init__(self)
class ChildB(Base):
def __init__(self):
super(ChildB, self).__init__()
ChildA()
ChildB()
#1 楼
super()
让您避免显式引用基类,这可能很好。但是主要优势来自多重继承,其中可以发生各种有趣的事情。 请注意关于super的标准文档。
请注意,Python 3.0中的语法已更改:您可以说
super().__init__()
而不是super(ChildB, self).__init__()
,这对IMO来说要好得多。标准文档还参考了有关使用super()
的指南,这非常具有解释性。#2 楼
我想了解
super()
我们使用
super
的原因是,使得可能正在使用协作多重继承的子类将调用正确的下一个父类方法解析顺序(MRO)中的类函数。在Python 3中,我们可以这样称呼它:
class ChildB(Base):
def __init__(self):
super().__init__()
在Python 2中,我们需要像这样使用它:
super(ChildB, self).__init__()
没有超级,使用多重继承的能力就会受到限制:
Base.__init__(self) # Avoid this.
我在下面进一步解释。
“此代码实际上有什么区别?:”
class ChildA(Base):
def __init__(self):
Base.__init__(self)
class ChildB(Base):
def __init__(self):
super(ChildB, self).__init__()
# super().__init__() # you can call super like this in Python 3!
该代码的主要区别在于,您在
__init__
中使用super
获得了一个间接层,该层使用当前类来确定要在MRO中查找的下一类的__init__
。我在一个规范问题(如何在Python中使用“ super”?)的答案中说明了这种差异。
如果Python没有
super
,这里的代码实际上与
super
等效(在C中如何实现,减去一些检查和后备行为,并转换为Python):class ChildB(Base):
def __init__(self):
mro = type(self).mro() # Get the Method Resolution Order.
check_next = mro.index(ChildB) + 1 # Start looking after *this* class.
while check_next < len(mro):
next_class = mro[check_next]
if '__init__' in next_class.__dict__:
next_class.__init__(self)
break
check_next += 1
写得更像本机Python:
class ChildB(Base):
def __init__(self):
mro = type(self).mro()
for next_class in mro[mro.index(ChildB) + 1:]: # slice to end
if hasattr(next_class, '__init__'):
next_class.__init__(self)
break
如果没有
super
对象,则必须在任何地方编写此手动代码(或重新创建它),以确保我们在“方法解析顺序”中调用正确的下一个方法!super如何在Python 3中做到这一点,而又没有明确告知调用方法的是哪个类和实例?
它获取调用堆栈框架,并找到类(隐式存储为局部自由变量
__class__
,使调用函数成为该类的闭包)和该函数的第一个参数,该参数应为通知它的实例或类。使用哪种方法解析顺序(MRO)。 因为它要求MRO的第一个参数,所以不可能将
super
与静态方法一起使用。其他答案的批评:
super()使您避免显式引用基类,这可能很好。 。但是主要优势来自多重继承,其中可以发生各种有趣的事情。如果您还没有的话,请参阅有关super的标准文档。
有点手摇且不能告诉我们太多,但是
super
的目的是避免避免编写父代类。关键是要确保调用方法解析顺序(MRO)中的下一个方法。这对于多重继承很重要。我将在这里进行解释。孩子:
class Base(object):
def __init__(self):
print("Base init'ed")
class ChildA(Base):
def __init__(self):
print("ChildA init'ed")
Base.__init__(self)
class ChildB(Base):
def __init__(self):
print("ChildB init'ed")
super(ChildB, self).__init__()
现在记住,
ChildB
使用超级,ChildA
不使用:class UserDependency(Base):
def __init__(self):
print("UserDependency init'ed")
super(UserDependency, self).__init__()
和
UserA
不会调用UserDependency方法:class UserA(ChildA, UserDependency):
def __init__(self):
print("UserA init'ed")
super(UserA, self).__init__()
class UserB(ChildB, UserDependency):
def __init__(self):
print("UserB init'ed")
super(UserB, self).__init__()
,但是
UserB
实际上会调用UserDependency,因为ChildB
会调用super
:>>> UserA()
UserA init'ed
ChildA init'ed
Base init'ed
<__main__.UserA object at 0x0000000003403BA8>
对另一个答案的批评
在任何情况下,您都不应执行以下建议,这是另一个答案所建议的,因为当您继承ChildB的子类时肯定会出错:
>>> UserB()
UserB init'ed
ChildB init'ed
UserDependency init'ed
Base init'ed
<__main__.UserB object at 0x0000000003403438>
(这个答案并不聪明,也不是特别有趣,但是尽管在评论中有直接批评和超过17次否决票,回答者仍然坚持建议,直到善良的编辑者解决了他的问题。)
说明:使用
self.__class__
代替super()
中的类名会破坏MRO并可能导致递归。 super
让我们在MRO中查找下一个父类(请参阅此答案的第一部分)以获取子类。如果您告诉super
我们在子实例的方法中,它将在行中查找下一个方法(可能是这个方法),从而导致递归,可能会导致逻辑故障(在回答者的示例中,确实如此),或者当RuntimeError
超出了递归深度。super(self.__class__, self).__init__() # DON'T DO THIS! EVER.
评论
我仍然需要努力解决这个super()函数,但是就深度和细节而言,这个答案显然是最好的。我也非常感谢答案中的批评。通过识别其他答案中的陷阱,也有助于更好地理解该概念。谢谢 !
– Yoo Obadia
17年5月29日在15:09
@Aaron Hall,感谢您提供如此详细的信息。我认为,如果导师没有提供正确的足够信息,他们应该有更多选择(至少)供导师称某些答案为不合适或不完整。
–预感
17年6月10日在14:56
谢谢,这超级有帮助。对使用不当/使用不当的批评非常说明了为什么以及如何使用超级
–氙气灯
18年4月6日19:00
我一直在super().__ init __()上使用tk.Tk .__ init __(self),因为我不完全了解super是什么,但是这篇文章很有启发性。我想在Tkinter类的情况下tk.Tk .__ init __(self)和super().__ init __()是同一回事,但看起来您是在说我们应该避免做类似Base .__ init __(self)的事情,所以我可能即使我仍在尝试掌握其复杂性,也可以切换到super()。
– Mike-SMT
18年11月8日15:55
这个答案特别全面,确实填补了我的知识空白。先生,向你致敬。
–Physiccubus
19年2月10日在20:26
#3 楼
需要注意的是,在Python 3.0+中,您可以使用super().__init__()
进行调用,这很简洁,不需要您明确引用父类或类名,这可能很方便。我只想补充一下,对于Python 2.7或更低版本,有些人通过编写
self.__class__
而不是类名来实现名称不敏感的行为,即super(self.__class__, self).__init__() # DON'T DO THIS!
但是,这中断从您的类继承的任何类的
super
的调用,其中self.__class__
可以返回子类。例如:class Polygon(object):
def __init__(self, id):
self.id = id
class Rectangle(Polygon):
def __init__(self, id, width, height):
super(self.__class__, self).__init__(id)
self.shape = (width, height)
class Square(Rectangle):
pass
这里我有一个类
Square
,它是Rectangle
的子类。说我不想为Square
写一个单独的构造函数,因为Rectangle
的构造函数已经足够好了,但是出于任何原因,我都想实现一个Square,这样我就可以重新实现其他方法。当我使用
Square
创建一个mSquare = Square('a', 10,10)
,Python调用Rectangle
的构造函数,因为我没有给Square
自己的构造函数。但是,在Rectangle
的构造函数中,调用super(self.__class__,self)
将返回mSquare
的超类,因此它将再次调用Rectangle
的构造函数。如@S_C所述,这就是无限循环的发生方式。在这种情况下,当我运行super(...).__init__()
时,我正在调用Rectangle
的构造函数,但是由于没有给它参数,所以会出现错误。评论
如果您再次提供子类而不提供新的__init__,则此答案表明super(self .__ class__,self).__ init __()不起作用。然后,您可以进行无限递归。
– glglgl
2014年3月31日7:21
这个答案太荒谬了。如果您打算以这种方式滥用超级代码,则最好对基类名称进行硬编码。比这少错了。 super的第一个论点的重点在于,它不一定是自我的类型。请阅读rhettinger的“超级被认为是超级”(或观看他的一些视频)。
–Veky
16年7月29日在12:54
这里为Python 2演示的快捷方式具有已经提到的陷阱。不要使用它,否则您的代码会以您无法预测的方式破坏。这个“方便的快捷方式”打破了常规,但是直到您花了很多时间进行调试后,您才可能意识到。如果super太冗长,请使用Python 3。
–瑞安·希伯特(Ryan Hiebert)
17年1月13日在16:17
编辑答案。抱歉,该编辑将含义更改为180度,但是现在这个答案应该有意义了。
– Tino
17年11月21日14:33
毫无意义的是告诉某人他们可以做一些被证明是不正确的事情。您可以将echo别名为python。没有人会建议!
–亚伦·霍尔♦
17 Dec 2'在23:18
#4 楼
超级没有副作用Base = ChildB
Base()
按预期工作
Base = ChildA
Base()
进入无限递归。
评论
在这种情况下,“超级没有副作用”的说法是没有意义的。 Super只是简单地保证我们按方法解析顺序调用正确的下一类方法,而另一种方法则硬编码要调用的下一方法,这使得协作多重继承更加困难。
–亚伦·霍尔♦
17-10-1在0:53
这个答案是零碎的(代码示例仅作为答案中代码的延续才有意义。)
– MarkHu
9月2日17:49
#5 楼
刚开始……使用Python 2.7,并且我相信自从在版本2.2中引入super()
以来,如果其中一个父类继承自最终继承super()
(新式类)的类,则您只能调用object
。 /> 就python 2.7代码而言,我个人将继续使用
BaseClassName.__init__(self, args)
,直到我真正获得使用super()
的好处为止。评论
很好的一点。如果您没有明确提及:Class Base(object):那么您将得到如下错误:“ TypeError:必须是类型,而不是classobj”
– andilabs
13年7月19日在11:38
@andi前几天我遇到了这个错误,我最终只是放弃试图解决这个错误。我只是在弄乱iPython。如果实际上是我必须调试的代码,那么错误消息的可怕噩梦!
–两位炼金术士
16年5月5日在13:56
#6 楼
真的没有。super()
查看MRO中的下一个类(方法解析顺序,使用cls.__mro__
访问)以调用方法。只需调用基本__init__
即可调用基本__init__
。碰巧的是,MRO只有一项-基础。因此,您实际上确实在做同样的事情,但使用super()
的方式更好(特别是如果以后要进行多重继承的话)。评论
我懂了。您能否详细说明为什么将super()与多重继承一起使用会更好吗?对我而言,base .__ init __(self)更短(更干净)。如果我有两个基类,那将是其中的两行或两行super()。还是我误解了您所说的“尼科尔”是什么意思?
–́Mizipzor
09年2月23日,0:40
实际上,这将是一条super()行。当您有多个继承时,MRO仍然是平坦的。因此,第一个super().__ init__调用将调用下一个类的init,然后再调用下一个类,依此类推。您应该真正查看一些文档。
– Devin Jeanpierre
09年2月23日,0:45
子类MRO也包含对象-类的MRO在mro类变量中可见。
–詹姆斯·布雷迪
09年2月23日在1:24
另请注意,经典类(2.2之前的版本)不支持super-您必须显式引用基类。
–詹姆斯·布雷迪
09年2月23日在1:26
“子类MRO也包含对象-一个类的MRO在mro类变量中可见。”真是个大麻烦。哎呀
– Devin Jeanpierre
09年2月23日在4:14
#7 楼
主要区别是ChildA.__init__
将无条件调用Base.__init__
,而ChildB.__init__
将在__init__
祖先行中的ChildB
祖先类别中任意调用self
(可能与您的期望不同)。
如果您添加一个使用多重继承的
ClassC
:class Mixin(Base):
def __init__(self):
print "Mixin stuff"
super(Mixin, self).__init__()
class ChildC(ChildB, Mixin): # Mixin is now between ChildB and Base
pass
ChildC()
help(ChildC) # shows that the Method Resolution Order is ChildC->ChildB->Mixin->Base
,则
Base
不再是ChildB
实例的ChildC
的父级。现在,如果super(ChildB, self)
是Mixin
实例,则self
将指向ChildC
。您已在
Mixin
和ChildB
之间插入了Base
。而且,您可以通过super()
来利用它,因此,如果您将类设计为可以在协作多重继承方案中使用,请使用super
,因为您实际上并不知道谁将成为祖先在运行时。超级视频和pycon 2015随附视频很好地解释了这一点。
评论
这个。 super(ChildB,self)的含义根据self所引用的对象的MRO改变,直到运行时才知道。换句话说,ChildB的作者无法知道在所有情况下super()都将解析为什么,除非他们可以保证永远不会对ChildB进行子类化。
–nispio
2015年11月13日下午4:17
评论
您能否提供将super()与参数一起使用的示例?
– Stevoisiak
18年2月14日在15:34
您能解释一下super(ChildB,self).__ init __()this,ChildB和self与super有什么关系
–rimalroshan
18年5月6日在5:22
@rimiro super()的语法是super([type [,object]])这将返回type的超类。因此,在这种情况下,将返回ChildB的超类。如果省略第二个参数,则返回的超级对象是未绑定的。如果第二个参数是一个对象,则isinstance(object,type)必须为true。
–欧姆尼克
18年8月30日在15:48
如果您在这里仍然感到困惑,请阅读亚伦·霍尔的回答,您将使此页面更加快乐:stackoverflow.com/a/27134600/1886357
–eric
19年2月18日在23:53
您可以实际解释该代码的作用吗?我不想再点击一百万个地方来找到答案。
–查理·帕克(Charlie Parker)
19年4月4日在23:00