<blockquote>
<h2>I'm trying to understand <code>super()</code></h2>
</blockquote>
<p>我们使用<code>super</code>的原因是,可能使用协作多重继承的子类将调用方法解析顺序(MRO)中正确的下一个父类函数。</p>
<p>在Python 3中,我们可以这样称呼它:</p>
<pre><code>class ChildB(Base):
def __init__(self):
super().__init__()
</code></pre>
<p>在Python 2中,我们需要这样使用它:</p>
<pre><code>super(ChildB, self).__init__()
</code></pre>
<p>如果没有super,您使用多重继承的能力将受到限制:</p>
<pre><code>Base.__init__(self) # Avoid this.
</code></pre>
<p>我在下面进一步解释。</p>
<blockquote>
<h2>"What difference is there actually in this code?:"</h2>
</blockquote>
<pre><code>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!
</code></pre>
<p>这段代码的主要区别在于在<code>__init__</code>中用<code>super</code>得到一个间接层,它使用当前类来确定要在MRO中查找的下一个类的<code>__init__</code>。</p>
<p>我在<a href="https://stackoverflow.com/a/33469090/541136">canonical question, How to use 'super' in Python?</a>的一个答案中说明了这种差异,它演示了依赖注入和合作多重继承。</p>
<h2>如果Python没有<code>super</code></h2>
<p>下面的代码实际上与<code>super</code>(它是如何在C中实现的,减去一些检查和回退行为,并转换为Python)非常相似:</p>
<pre><code>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
</code></pre>
<p>写得有点像原生Python:</p>
<pre><code>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
</code></pre>
<p>如果我们没有<code>super</code>对象,我们将不得不在任何地方编写此手动代码(或者重新创建它!)以确保在方法解析顺序中调用正确的下一个方法!</p>
<p>super如何在Python 3中做到这一点,而不被明确告知它是从哪个类和实例调用的?</p>
<p>它获取调用堆栈帧,并找到类(隐式存储为本地自由变量,<code>__class__</code>,使调用函数成为类的闭包)和该函数的第一个参数,该参数应该是通知它要使用哪个方法解析顺序(MRO)的实例或类。</p>
<p>因为它需要MRO的第一个参数<a href="https://bugs.python.org/issue31118" rel="noreferrer">using ^{<cd1>} with static methods is impossible</a>。</p>
<h2>对其他答案的批评:</h2>
<blockquote>
<p>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.</p>
</blockquote>
<p>这是一个很难理解的问题,但是<code>super</code>的重点并不是避免编写父类。关键是确保调用方法解析顺序(MRO)中的下一个方法。这在多重继承中变得很重要。</p>
<p>我在这里解释。</p>
<pre><code>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__()
</code></pre>
<p>让我们创建一个依赖项,希望在子对象之后调用它:</p>
<pre><code>class UserDependency(Base):
def __init__(self):
print("UserDependency init'ed")
super(UserDependency, self).__init__()
</code></pre>
<p>现在记住,<code>ChildB</code>使用super,<code>ChildA</code>不:</p>
<pre><code>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__()
</code></pre>
<p>并且<code>UserA</code>不调用UserDependency方法:</p>
<pre><code>>>> UserA()
UserA init'ed
ChildA init'ed
Base init'ed
<__main__.UserA object at 0x0000000003403BA8>
</code></pre>
<p>但是<code>UserB</code>,因为<code>ChildB</code>使用<code>super</code>,是的!以下内容:</p>
<pre><code>>>> UserB()
UserB init'ed
ChildB init'ed
UserDependency init'ed
Base init'ed
<__main__.UserB object at 0x0000000003403438>
</code></pre>
<h3>对另一个答案的批评</h3>
<p>在任何情况下,您都不应该执行以下操作,这是另一个答案所建议的,因为当您将ChildB子类化时,您肯定会出错:</p>
<pre><code>super(self.__class__, self).__init__() # Don't do this. Ever.
</code></pre>
<p><sub>(这个答案不聪明,也不特别有趣,但尽管评论中有直接的批评和超过17次的反对票,回答者还是坚持建议,直到一位好心的编辑解决了他的问题。)</sub></p>
<p>说明:这个答案建议这样称呼super:</p>
<pre><code>super(self.__class__, self).__init__()
</code></pre>
<p>这是完全错误的。<code>super</code>让我们在MRO中查找子类的下一个父类(请参阅此答案的第一部分)。如果告诉<code>super</code>我们在子实例的方法中,那么它将在第行(可能是这一行)中查找下一个方法,从而导致递归,当超过递归深度时,可能导致逻辑失败(在应答器的示例中,它确实发生了)或<code>RuntimeError</code>。</p>
<pre><code>>>> 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'
</code></pre>