我想了解一下 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__with中获得了一个间接层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'edBase init'ed
<__main__.UserA object at 0x0000000003403BA8>
但是UserB,因为ChildB使用super,确实!:>>> UserB()UserB init'ed
ChildB init'edUserDependency init'ed
Base init'ed<__main__.UserB object at 0x0000000003403438>
对另一个答案的批评
在任何情况下都不应该执行以下操作,这是另一个答案所暗示的,因为当您继承ChildB时肯定会出错:super(self.__class__, self).__init__() # Don't do this. Ever.
(这个答案并不聪明或特别有趣,但尽管评论中有直接的批评和超过17个downvotes,但回答者坚持提出建议,直到一位善良的编辑解决了他的问题。)
说明:那个回答建议像这样调用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 "", line 1, in
File "", line 3, in __init__TypeError: __init__() missing 2 required positional arguments: 'width' and 'height'