用__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,可能会导致逻辑失败(在回答者的例子中,它会)或RuntimeError
recursion深度被超过。
>>> 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
实例的话。
您在ChildB
和Base
之间插入了Mixin
。 你可以利用它与super()
所以,如果您devise了类,以便可以在合作多重inheritancescheme中使用它们,则可以使用super
因为您不知道在运行时谁将成为祖先。
超级考虑超级邮政和pycon 2015伴随video解释这很好。