我正在努力了解它的用法 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()
super()
让你避免显式引用基类,这可能很好。但主要的优点是多重继承,各种各样的 好玩的东西 可以发生。见 超级标准文档 如果你还没有。
注意 Python 3.0中的语法已更改:你可以说 super().__init__()
代替 super(ChildB, self).__init__()
哪个IMO好一点。
我想了解 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
,它使用当前类来确定下一个类 __init__
在MRO中查找。
我在答案中说明了这种差异 规范问题,如何在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
对象,我们必须在任何地方编写这个手动代码(或重新创建它!),以确保我们在方法解析顺序中调用正确的下一个方法!
超级如何在没有明确告知调用它的方法中的哪个类和实例的情况下在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__()
让我们创建一个我们希望在Child之后调用的依赖项:
class UserDependency(Base):
def __init__(self):
print("UserDependency init'ed")
super(UserDependency, self).__init__()
现在记住, ChildB
使用超级, ChildA
才不是:
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__()
和 UserA
不调用UserDependency方法:
>>> UserA()
UserA init'ed
ChildA init'ed
Base init'ed
<__main__.UserA object at 0x0000000003403BA8>
但 UserB
因为 ChildB
使用 super
,确实!:
>>> UserB()
UserB init'ed
ChildB init'ed
UserDependency init'ed
Base init'ed
<__main__.UserB object at 0x0000000003403438>
对另一个答案的批评
在任何情况下都不应该执行以下操作,这是另一个答案所暗示的,因为当您继承ChildB时肯定会出错:
super(self.__class__, self).__init__() # Don't do this. Ever.
(这个答案并不聪明或特别有趣,但尽管评论中有直接的批评和超过17个赞成票,但回答者坚持提出建议,直到一位善良的编辑解决了他的问题。)
说明:那个回答建议像这样调用super:
super(self.__class__, self).__init__()
这是 全然 错误。 super
让我们在子类中查找MRO中的下一个父级(请参阅本答案的第一部分)。如果你告诉 super
我们在子实例的方法中,它将在行中查找下一个方法(可能是这个)导致递归,可能导致逻辑失败(在回答者的例子中,它确实)或者 RuntimeError
当超过递归深度时。
>>> 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('a', 10, 10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __init__
TypeError: __init__() missing 2 required positional arguments: 'width' and 'height'
已经注意到在Python 3.0+中你可以使用
super().__init__()
进行调用,这是简洁的,并且不需要您明确地引用父OR类名称,这可能很方便。我只想在Python 2.7或更低版本中添加它,通过编写可以获得这种对名称不敏感的行为 self.__class__
而不是类名,即
super(self.__class__, self).__init__()
然而,这打破了电话 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
但既然我没有提出任何论据,我会得到一个错误。
只是一个单挑......用Python 2.7,我相信从那以后 super()
在2.2版中推出,你只能打电话 super()
如果其中一个父母继承自最终继承的类 object
(新式课程)。
就个人而言,对于python 2.7代码,我将继续使用 BaseClassName.__init__(self, args)
直到我真正获得使用的优势 super()
。
真的没有。 super()
查看MRO中的下一个类(方法解析顺序,使用 cls.__mro__
)调用方法。只是打电话给基地 __init__
打电话给基地 __init__
。碰巧的是,MRO只有一个项目 - 基础。所以你真的在做同样的事情,但是用更好的方式 super()
(特别是如果你以后进入多重继承)。
主要区别在于 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 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伴随视频 解释得很好。