如何在 Python 中调用超级构造函数?

Posted

技术标签:

【中文标题】如何在 Python 中调用超级构造函数?【英文标题】:How to invoke the super constructor in Python? 【发布时间】:2011-01-24 20:27:54 【问题描述】:
class A:
    def __init__(self):
        print("world")

class B(A):
    def __init__(self):
       print("hello")

B()  # output: hello

在我使用过的所有其他语言中,超级构造函数被隐式调用。如何在 Python 中调用它?我希望super(self) 但这不起作用。

【问题讨论】:

您应该强调不使用派生类名称的答案是您想要的。例如(伪代码):super().__init__(args...) 您应该接受 Aidan Gomez 的回答。它会为我们节省很多时间,因为它在 python 2 和 3 中都有答案。 Python 2 不再被官方支持。他的答案也在 5 年后出现。 @Mike 我认为列出 Python 2 方式的答案仍然有价值,因为那里有很多旧的 Python 2 代码,而且有些人最终会回答这个问题否则可能不知道如何理解它。 (尽管它已停产,但许多人确实仍然在 Python 2 中编写代码,要么是因为他们不了解,要么是因为某些组织要求迫使他们这样做。) 我已将接受的答案更改为@Aiden Gomez 的答案。尽管 Ignacio 是正确的,但鉴于 Python 3 对 super() 的更改,@Aidan 是迄今为止最合适的 【参考方案1】:

与其他答案一致,有多种方法可以调用超类方法(包括构造函数),但是在 Python-3.x 中,该过程已被简化:

Python-2.x

class A(object):
 def __init__(self):
   print "world"

class B(A):
 def __init__(self):
   print "hello"
   super(B, self).__init__()

Python-3.x

class A(object):
 def __init__(self):
   print("world")

class B(A):
 def __init__(self):
   print("hello")
   super().__init__()

super() 现在等同于super(<containing classname>, self),根据the docs。

【讨论】:

【参考方案2】:

super() 在新式类中返回一个类父对象

class A(object):
    def __init__(self):
        print("world")

class B(A):
    def __init__(self):
        print("hello")
        super(B, self).__init__()

B()

【讨论】:

只是出于好奇,为什么super(B,self) 要求同时提及 B 和 self?这不是多余的吗? self 不应该包含对 B 的引用吗? 不,因为self 实际上可能是C 的一个实例,是B 的一个子级。 @Luc:那是因为类声明不正确。看我的回答。 对于documentation of super(),你应该可以写super().__init__() 没有参数。 @JojOatXGME:在 3.x 中,是的。 2.x 仍然需要参数。【参考方案3】:

对于 Python 2.x 旧式类,它会是这样的:

class A: 
 def __init__(self): 
   print "world" 

class B(A): 
 def __init__(self): 
   print "hello" 
   A.__init__(self)

【讨论】:

@kdbanman:这将适用于新型类,但使用新型类的原因之一是不必这样做。您可以使用super,而不必直接命名您要继承的类。 @Gabe 这是使用super 的最不重要的原因之一。【参考方案4】:

一种方法是调用 A 的构造函数并将self 作为参数传递,如下所示:

class B(A):
    def __init__(self):
        A.__init__(self)
        print "hello"

这种风格的优点是非常清晰。它调用 A 的初始化程序。缺点是它不能很好地处理菱形继承,因为您最终可能会调用共享基类的初始化程序两次。

另一种方法是使用 super(),正如其他人所展示的那样。对于单继承,它的作用与让您调用父级的初始化程序基本相同。

然而,super() 在底层要复杂得多,并且在多重继承的情况下有时会违反直觉。从好的方面来说,super() 可用于处理菱形继承。如果您想了解 super() 的具体作用,我发现的关于 super() 工作原理的最佳解释是 here(尽管我不一定赞同那篇文章的观点)。

【讨论】:

当A的构造函数不接受任何变量(忽略self)作为参数时,是否需要像这样调用A的构造函数?我的意思是代码可以在没有A.__init__(self) 行的情况下正常工作。 @RedFloyd 这取决于A.__init__ 的实际作用。它可能会以恒定的方式初始化各种内部属性(例如self.stuff = []),如果您不调用A.__init__,这可能会导致您的B 实例无法正常运行。【参考方案5】:

简答

super(DerivedClass, self).__init__()

长答案

super() 是做什么的?

它获取指定的类名,找到它的基类(Python 允许多重继承)并从左到右在每个基类中查找方法(在这种情况下为__init__)。一旦找到可用的方法,它就会调用它并结束搜索。

如何调用所有基类的 init?

如果您只有一个基类,上述方法有效。但是 Python 确实允许多重继承,您可能希望确保所有基类都正确初始化。为此,您应该让每个基类都调用 init:

class Base1:
  def __init__():
    super(Base1, self).__init__()

class Base2:
  def __init__():
    super(Base2, self).__init__()

class Derived(Base1, Base2):
  def __init__():
    super(Derived, self).__init__()

如果我忘记为 super 调用 init 怎么办?

构造函数 (__new__) 在链中被调用(如在 C++ 和 Java 中)。创建实例后,仅调用该实例的初始化程序 (__init__),而没有任何指向其超类的隐式链。

【讨论】:

__new__ 也没有被链接。如果您的示例中的所有三个类都定义了__new__,但没有使用super().__new__(),那么Derived() 将调用Derived.__new__,而不是Base1.__new__Base2.__new__。但是,不调用其父级的__new____new__ 几乎是无用的,因为最终object.__new__ 是唯一真正创建任何新实例的东西。 PyLint 建议使用 Python 3 风格的 super() 不带参数【参考方案6】:

只是添加一个带参数的例子:

class B(A):
    def __init__(self, x, y, z):
        A.__init__(self, x, y)

给定一个需要定义变量 x、y、z 的派生类 B,以及一个需要定义 x、y 的超类 A,您可以调用具有对当前子类实例 (self) 的引用的超类 A,然后是预期参数列表。

【讨论】:

这是一个更正确的答案,因为呈现为 super() 的类可以更改...这清楚地表明您要调用哪个构造函数【参考方案7】:

我使用以下公式来扩展以前的答案:

class A(object):
 def __init__(self):
   print "world"

class B(A):
 def __init__(self):
   print "hello"
   super(self.__class__, self).__init__()

B()

这样您就不必在调用 super 时重复类的名称。如果您正在编写大量类,并且希望在初始化方法中使您的代码独立于类名,它会派上用场。

【讨论】:

但是如果你决定用C扩展B,你会得到不定式递归,并且self.__class__指向C(B)而不是B(A),所以super(self.__class__, self)指向@987654328 @ 而不是 A 当你继承B 时,这将导致无限递归错误。 不要使用self.__class__type(self),要么传入显式类(此处为B),要么在Python 3中使用不带参数的super()

以上是关于如何在 Python 中调用超级构造函数?的主要内容,如果未能解决你的问题,请参考以下文章

如何在龙目岛调用超级构造函数

如何:安全地调用具有不同参数的超级构造函数

调用超级构造函数时尝试资源

爪哇。隐式超级构造函数 Employee() 未定义。必须显式调用另一个构造函数[重复]

隐式超级构造函数 Person() 未定义。必须显式调用另一个构造函数?

如何在主方法中从类中调用构造函数