我试图理解
我很想知道以下两个子类的实际区别。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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() |
注意,Python 3.0中的语法发生了变化:您可以只说
I'm trying to understand super()
我们使用
在python3中,我们可以这样调用它:
1 2 3 | class ChildB(Base): def __init__(self): super().__init__() |
在python2中,我们需要这样使用它:
1 | super(ChildB, self).__init__() |
如果没有super,你使用多重继承的能力就会受到限制:
1 | Base.__init__(self) # Avoid this. |
我在下面进一步解释。
"What difference is there actually in this code?:"
1 2 3 4 5 6 7 8 | 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! |
这段代码的主要区别在于,您在
我在一个典型问题的答案中说明了这种差异,即如何在Python中使用"super"?,演示依赖项注入和协作多重继承。
如果Python没有下面的代码实际上与
1 2 3 4 5 6 7 8 9 10 | 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:
1 2 3 4 5 6 7 | 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 |
如果我们没有
在python3中,super是如何做到这一点的,而不需要显式地告诉它是从哪个方法调用的类和实例?
调用堆栈帧,并发现类(隐式存储为当地自由变量,
因为它需要MRO的第一个参数,所以在静态方法中使用
super() lets you avoid referring to the base class explicitly, which can be nice. . But the main advantage comes with multiple inheritance, where all sorts of fun stuff can happen. See the standard docs on super if you haven't already.
它相当手摇,没有告诉我们太多,但是
我将解释。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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__() |
让我们创建一个依赖项,我们希望在子元素之后调用它:
1 2 3 4 | class UserDependency(Base): def __init__(self): print("UserDependency init'ed") super(UserDependency, self).__init__() |
现在请记住,
1 2 3 4 5 6 7 8 9 | 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__() |
并且
1 2 3 4 5 | >>> UserA() UserA init'ed ChildA init'ed Base init'ed <__main__.UserA object at 0x0000000003403BA8> |
但是
1 2 3 4 5 6 | >>> UserB() UserB init'ed ChildB init'ed UserDependency init'ed Base init'ed <__main__.UserB object at 0x0000000003403438> |
批评另一个答案
在任何情况下都不应该这样做,这是另一个答案的建议,因为子类化ChildB时肯定会出错:
1 | super(self.__class__, self).__init__() # Don't do this. Ever. |
(这个答案既不聪明也不特别有趣,但是尽管在评论中有直接的批评,而且有超过17次的反对票,这个答案还是坚持建议,直到一位好心的编辑解决了他的问题。
解释:这个答案建议这样称呼super:
1 | super(self.__class__, self).__init__() |
这是完全错误的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | >>> 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+中可以使用
调用,这是简洁的,不需要显式引用父类名或类名,这很方便。我只想补充一点,对于Python 2.7或更低版本,可以通过编写
1 | super(self.__class__, self).__init__() |
但是,这中断了对继承自您的类的任何类的
1 2 3 4 5 6 7 8 9 10 11 | 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 |
这里有一个类
当我使用
Super没有副作用
1 2 3 | Base = ChildB Base() |
像预期的那样工作
1 2 3 | Base = ChildA Base() |
进入无限递归。
请注意……使用Python 2.7,而且我相信自从在2.2版本中引入了
就我个人而言,对于python 2.7代码,我将继续使用
没有,真的。
主要的区别是
如果添加一个使用多重继承的
1 2 3 4 5 6 7 8 9 10 | 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 |
那么对于
您在
因此,如果您将类设计为可以在协作多继承场景中使用,那么您将使用
super considered的super post和pycon 2015的视频很好地解释了这一点。