调用内置 class 的 __init__() 和用户定义 class 的 __init__() 的区别

Difference between calling __init__() of build-in class and __init__() of user-defined class

我正在学习 python classes 并且无法理解以下行为:

在下面的示例中,我正在扩展内置 str class:

class uStr(str):
    def __init__(self,u_str):
        str.__init__(u_str)       #<<<------- no self is needed

    def underline(self):
        underlines = format('', '-^' + str(len(self)))
        return str.__str__(self) + '\n' + underlines

在下面的例子中,我扩展了一个用户定义的 class Fraction:

from fraction import *

class MixedFraction(Fraction):
    def __init__(self, *args):
        Fraction.__init__(self, args[0], args[1])     #<<<-------self is needed

    def getWholeNum(self):
        return self.getNumerator() // self.getDenominator()

为什么我们需要在第二个例子中调用 __init__ 的 super class 作为参数,而在调用时不需要给出 self str__init__.

首先,您根本不需要那些 __init__ 实现。您可以只继承超类实现。如果您确实需要自定义构造,那么对于 uStr,您应该在 __new__ 而不是 __init__.

中进行

str doesn't have 自己的 __init__。它在 __new__ 中进行所有初始化,因为 str 实例是不可变的,而在 __init__ 中初始化是可变的。 strobject.

继承 __init__

object.__init__object.__new__ 有点奇怪。如果恰好 object.__init__object.__new__ 中的一个被覆盖,那么如果给定参数(超出 self),被覆盖的一个将抛出错误,而另一个忽略参数,以节省您必须做的工作提供一个无所事事的覆盖。但是,如果两者都被覆盖或两者都没有被覆盖,那么如果给定参数(超出 self),两者都会抛出错误。您可以在源代码的 big comment 中看到这一点的解释。

str 实现了 __new__,但从 object 继承了 __init__。当您覆盖 __init__ 并调用 str.__init__ 时,您实际上是在调用 object.__init__.

当您调用 str.__init__(u_str) 时,您实际上是在 object.__init__ 调用 u_str 错误的对象,而不是 self。因为 u_str 没有 __init__ 覆盖(并且因为你只传递一个参数,它被解释为 self),object.__init__ 默默地什么都不做。

当您调用 str.__init__(self, u_str) 时,您正在为 self 调用 object.__init__,但是由于 self 同时具有 __new____init__ 被覆盖,object.__init__ 抱怨 u_str 参数。

看起来您实际上根本不需要在子类中重写构造。如果这样做,自定义构造 str 子类的正确方法是覆盖 __new__。如果由于某些奇怪的原因你不得不调用 str.__init__,正确的调用应该是 str.__init__(self)super().__init__()。由于 object.__init__ 不进行任何初始化,您也可以省略超类构造函数调用。