用__init __()方法理解Python super()方法

我想了解super() 。 从外观上看,这两个子类都可以创build得很好。 我很好奇下面的孩子class之间实际上有什么区别:

 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()可以让你避免直接引用基类,这可以很好。 但主要的优势来自多重inheritance,可以发生各种有趣的事情。 如果还没有,请参阅super上的标准文档 。

请注意, 在Python 3.0中更改了语法 :您可以只说super().__init__()而不是super(ChildB, self).__init__()哪个IMO比较好。

我想了解super()

我们使用super的原因是,可能使用协同多重inheritance的子类将在方法parsing顺序(MRO)中调用正确的下一个父类函数。

在Python 3中,我们可以这样调用它:

 class ChildB(Base): def __init__(self): super().__init__() 

在Python 2中,我们需要像这样使用它:

  super(ChildB, self).__init__() 

没有超级,你在使用多重inheritance的能力是有限的:

  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中使用“超级”? ,它演示了dependency injection协作多重inheritance

如果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对象,那么我们必须在任何地方编写这个手册代码(或重新创build它!)以确保我们在方法parsing顺序中调用正确的下一个方法!

超级如何在Python 3中做到这一点,而不是明确地告诉它被调用的方法的哪个类和实例?

它获取调用堆栈框架,并find该类(隐式存储为本地自由variables, __class__ ,使调用函数成为类的闭包)以及该函数的第一个参数,它应该是通知它的实例或类使用哪种方法parsing顺序(MRO)。

因为它需要MRO的第一个参数,所以使用super静态方法是不可能的 。

批评其他答案:

super()可以让你避免直接引用基类,这可以很好。 。 但主要的优势来自多重inheritance,可以发生各种有趣的事情。 如果还没有,请参阅super上的标准文档。

这是相当手动的,并没有告诉我们很多,但super目的不是要避免写父类。 重点是确保调用方法parsing顺序(MRO)中的下一个方法。 这在多重inheritance中变得重要。

我会在这里解释。

 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__() 

让我们创build一个我们希望在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> 

批评另一个答案

在任何情况下,你都不应该做下面的问题,而另一个答案build议,因为当你子类ChildB时,你肯定会得到错误:

  super(self.__class__, self).__init__() # Don't do this. Ever. 

(这个答案不是很聪明或者特别有意思,但是尽pipe直接批评了这个评论和17个投票,回答者仍然坚持build议,如果允许删除不正确的答案,我会投票删除那个答案。 )

说明:这个答案build议像这样调用超级:

 super(self.__class__, self).__init__() 

这是完全错误的。 super让我们查看MRO中的下一个父级(参见本答案的第一部分)。 如果你告诉super我们在子实例的方法中,它会查找下一个方法(可能是这个),导致recursion,可能会导致逻辑失败(在回答者的例子中,它会)或RuntimeErrorrecursion深度被超过。

 >>> 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__()

然而,如果你使用多层次的inheritance,那么这会中断,其中self.__class__可以返回一个子类—这意味着你和任何其他人都不能从使用这种模式的类inheritance。 例如:

 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,所以我可以重新实现其他一些方法。

当我使用mSquare = Square('a', 10,10)创build一个Square ,Python调用Rectangle的构造函数,因为我没有给它自己的构造函数。 但是,在Rectangle的构造函数中, super(self.__class__,self)将返回mSquare的超类,因此再次调用Rectangle的构造函数。 这就是@S_C所提到的无限循环的发生。 在这种情况下,当我运行super(...).__init__()我打电话给构造函数为Rectangle但因为我没有参数,我会得到一个错误。

超级没有副作用

 Base = ChildB Base() 

按预期工作

 Base = ChildA Base() 

进入无限recursion。

刚刚起来…与Python 2.7,我相信从2.2版本引入super() ,你只能调用super()如果其中一个父母从一个类最终inheritanceobject ( 新风格的类 )。

就个人而言,至于python 2.7代码,我将继续使用BaseClassName.__init__(self, args)直到我真正得到使用super()的优势。

没有,真的。 super()查看MRO中的下一个类(方法parsing顺序,通过cls.__mro__访问)来调用方法。 只要调用基地__init__调用基地__init__ 。 事实上,MRO只有一个项目 – 基础。 所以你真的在做同样的事情,但用super()更好的方式(特别是如果你稍后进入多重inheritance)。

主要区别在于ChildA.__init__将无条件地调用Base.__init__ChildB.__init__将调用__init__任何类的祖先中的ChildB祖先 (可能与您期望的不同)。

如果您添加使用多重inheritance的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不再是 ChildC实例ChildB的父代 。 现在super(ChildB, self)会指向Mixin如果self是一个ChildC实例的话。

您在ChildBBase之间插入了Mixin 。 你可以利用它与super()

所以,如果您devise了类,以便可以在合作多重inheritancescheme中使用它们,则可以使用super因为您不知道在运行时谁将成为祖先。

超级考虑超级邮政和pycon 2015伴随video解释这很好。