【问题标题】:Python convention: should I normally call the super class' __init__?Python 约定:我通常应该调用超类的 __init__ 吗?
【发布时间】:2015-05-16 16:39:57
【问题描述】:

我阅读了this question 的评分最高的答案,它说如果需要,我们应该调用超级类'__init__,而我们不必这样做。但我的问题更多是关于惯例。

我是否应该通常作为一般规则,总是在我的班级'__init__ 中调用超类'__init__,无论我当前是否“需要”该方法中的功能?

【问题讨论】:

  • 请不要投票结束这个问题。我可以看到它值得关闭其他语言。然而,Python 非常强调编码约定和什么是“Pythonic”,包括通常应该有一种正确的编写方式的想法;这就是为什么我认为这是一个有效的问题,本质上是询问是否默认包含对超类的 init 的调用是否是 Pythonic。
  • @Ghopper21 感谢支持,请投票重开。
  • 如果这是一个约定,您可能不必如此努力地搜索以获得答案。似乎是一个基于意见的问题:有时调用super().__init__方便,其他时候您可以手动进行成员初始化。

标签: python class convention


【解决方案1】:

是的。作为一般规则,您应该从子类的__init__ 方法调用超类的__init__。这不是 Python 约定,而是 OO 最佳实践建议您应该做的事情(鉴于您碰巧使用的语言,这个决定由您决定):

超类不知道子类,但子类应该知道它所继承的超类的语义。最终,要由子类来维护真正的sub-typing 的一致行为(不幸的是,Python 语言对程序员的帮助很小)。作为子类实现者,您可以决定是否需要调用超类__init__,就像您可以决定是否需要/不需要/想要调用您覆盖的任何方法的超类实现一样。但是,对象的初始化往往是许多对象生命周期中非常重要的一步。在对象被初始化之前,人们可能会认为该对象并不是给定类的真正实例。在所有理智的 OO 语言中,这是一个隐含且重要的后置条件,即当您实例化一个对象时,某些事情已经发生,我们可以依赖那些已经发生的事情,初始化(或其他语言中的“构造”)语言)是核心 - 无论它涉及复杂的参数验证和计算值生成,还是只是一个空操作。因此,如果您不调用 super 的初始化程序,您最好确切地知道您的用途。

作为一般规则调用 super 的 __init__ 的另一个论点是,如果你没有编写超类,或者你没有严格控制版本,__init__ 的实现将来可能会改变(确实在 @ 添加了一些东西987654328@ 要求增加主要版本号?我不确定,但我敢打赌很多人不会为此增加主要版本号,即使从技术上讲他们应该这样做)。因此,如果您确实调用 super 的 __init__,您的代码不太可能因更新超类实现而中断。


更新:在回答这个问题之前应该阅读linked question。那里的大多数答案都与这里的普遍情绪相呼应-也许不是以相同的术语或强烈支持将__init__()作为我的一般规则。但我会把这个答案留给其他人。

【讨论】:

  • 我同意。您可以编写有效 Python 代码而不调用上游__init__,但如果您不这样做,您的对象可能无法正确初始化。
  • 提问者不接受我的回答,因此您可能需要调整您的陈述。 (除非您认为现在接受的答案充其量是误导性的。)
  • 感谢您的提醒。声明已调整。
【解决方案2】:

这个答案有一些反对意见,因为反对者不同意我对焦点的看法,也许还有“惯例”的含义。我认为我们在编写代码时大多同意实际做法。


不。你不应该通常作为一般规则,总是在你的班级的__init__中调用超类的__init__无论是否不是您目前“需要”该方法中的功能

但请注意,我的重点是最后一个短语,以“无论如何”开头,而 那个 是我的“否”回答的意思。你不应该“仅仅因为有人告诉你”或“仅仅因为这似乎是大多数人正在做的事情”而在 Python 代码中添加一些东西。

如果需要,您应该包含某些内容,如果不需要,则不要包含某些内容。

这种情况经常发生,有些人会争辩说通常是这种情况,您确实想在子类的@中调用超类的__init__方法987654325@ 方法。 大部分时间我都是自己做的。

但是为什么呢?

至关重要的是,它不是因为一些“惯例”。我这样做是因为我的子类通常需要与超类相同的初始化,以及一些额外的自定义。请注意,额外的自定义是首先覆盖__init__ 的全部原因。如果您的子类的初始化与超类的初始化相同,那么您根本不应该定义自己的 __init__


编写不需要的代码并不是 Python 的惯例。有些人有他们自己的约定来包含不必要的东西;可能是以“防御性编程”的名义,或者因为它们习惯于需要更多样板的不同语言。

当您可以在多种方式之间进行选择来表达有用的东西时,Python 的约定就派上用场了。确实,Python 并不强调简洁高于一切。但这并不意味着它也强调冗长。所以让我补充一下,以防不清楚:

您应该通常作为一般规则,始终避免不必要的样板代码。 (不仅仅是在 Python 中。)

[对于那些认为“通常总是”这个词很尴尬或荒谬的人:我完全同意,但我试图通过重复提问者自己选择的词来强调我的观点。] p>

【讨论】:

  • “甚至可能是一个约定……”。调用基类的new if you override it 是惯例。然而你不需要。所以我认为有人很容易认为 init 也是如此。
  • 如果语义需要,它不是样板文件。我觉得这个答案混淆了一些概念:如果您所做的只是打电话给父母的__init__,请不要覆盖__init__。 “通常”并不意味着“总是”。我想说您需要覆盖 __init__ 但不需要(或不应该)调用上游 __init__ 的情况很少见。
  • @chepner - 我同意 100% 如果语义需要它不是样板文件。事实上,这就是我回答的全部要点。我想我从字面上解释了这个问题。即它是一个约定吗?对我来说,约定是没有内在理由的东西,除了顺其自然。 4 空格缩进和使用 self(而不是 this 或其他)是约定的示例。这些是 Python 中非常强大的约定,但没有内在的理由必须(甚至应该)这样。我已经编辑了答案,希望能让我的意图更清楚。
  • @spinkus - 如果您不需要,我不认为使用__new__ 是惯例。据我所知,您链接到的文档也不建议您这样做。
  • 我说得尽我所能。当然,样板代码是您必须自己编写而不是为您生成的必要代码。 Python 中的不变量,如果要调用它,则必须显式调用覆盖的方法;对于__init__,有明确的时间(为了您的代码正常运行),必须调用父类的__init__调用__init__ 意味着您仅从object 继承,并且您知道(或正在指定)您的类将不会在协作继承层次结构中使用。
【解决方案3】:

有些类需要调用它们的__init__ 方法才能工作。他们使用他们的__init__ 方法设置需要的属性。

例子:

class one ():
    def __init__ (self):
        self.number = 20
    def show_number(self):
        return self.number

如果你从上面的类继承,你需要调用它的__init__方法来定义属性number。如果未调用 __init__ 方法,则在尝试调用方法 show_number 时可能会出错。

关于语法,如果继承类的__init__ 方法没有发生任何事情,则不需要调用它。如果你认为不调用 __init__ 方法会混淆其他人,你可以随时用 cmets 解释你的推理。即使您不需要它,调用它也没有任何害处。

【讨论】:

    猜你喜欢
    • 2010-11-26
    • 2011-01-03
    • 2019-02-26
    • 1970-01-01
    • 2020-12-15
    • 1970-01-01
    • 2011-02-06
    • 2011-09-26
    • 1970-01-01
    相关资源
    最近更新 更多