【问题标题】:Python: must __init__(self, foo) always be followed by self.foo = foo?Python: __init__(self, foo) 必须始终跟在 self.foo = foo 之后吗?
【发布时间】:2012-03-16 00:48:50
【问题描述】:

三天来,我一直在努力学习__init__ 和“自我”,从Learn Python the Hard Way 练习42 开始,然后继续阅读部分Python 文档Alan Gauld's chapter on Object-Oriented ProgrammingStack threads like this one on "self"and this one,坦率地说,我正准备用砖头打自己的脸,直到我昏倒。

话虽如此,我注意到在最初的__init__ 定义中有一个非常常见的约定,即跟进 (self, foo) 然后立即在该定义中声明 self.foo = foo。

来自 LPTHW,ex42:

class Game(object): 
    def __init__(self, start): 
        self.quips = ["a list", "of phrases", "here"]
        self.start = start

来自艾伦·高德:

def __init__(self,val): self.val = val

我在那个可怕的空间里,我可以看到只有一件大事我没有得到,而且无论我读了多少并试图弄清楚它,它仍然是不透明的。也许如果有人可以向我解释这种一点点的一致性,灯就会亮起来。这是因为我们需要说变量“foo”总是等于 (foo) 参数,它本身包含在“self”参数中,该参数自动分配给它所附加的 def?

【问题讨论】:

    标签: python init self


    【解决方案1】:

    您可能想学习面向对象的编程

    简单地说,当你说时

    class Game(object):
        def __init__(self, start):
            self.start = start
    

    你是说:

    • 我有一个名为Game

    • “事物”类型
    • 每当创建一个新的Game 时,它都会要求我提供一些额外的信息,start。 (这是因为Game 的初始化程序,名为__init__,要求提供此信息。)

    • 初始化器(也称为“构造器”,虽然这是一个轻微用词不当)需要知道哪个对象(它是刚刚创建的前)它正在初始化。这是第一个参数——按照惯例,它通常称为self(但您可以将其称为其他任何名称……)。

    • 游戏可能需要记住我给它的start 是什么。因此,它通过创建一个名为 start 的实例变量 also(没什么特别的,它只是你想要的任何名称)并分配值,将这些信息存储在自身“内部”将start 参数转换为start 变量。

      • 如果它不存储参数的值,它就没有该信息可供以后使用。

    希望这能解释发生了什么。

    【讨论】:

    • 重要的是,您可以将参数分配给任何属性,例如self.abc。以相同的方式命名属性只是代码结构的问题。
    • @FelixKling:我做了一些小改动,让它更清楚一点,谢谢。
    • 我想我快到了,但可以对您的第三点提供一点帮助:您是说代码块的第三行可能是“self.anything = start”或“ self.start = 什么”?显然,应该与括号中的 init 参数一致,但我不确定哪个是实例变量,哪个是该行中的参数。抱歉,我是个笨蛋。
    • @mattshepherd:实例变量可以是任何东西(self.anything),但参数显然必须匹配(start)。
    【解决方案2】:

    我不太确定你缺少什么,所以让我来看看一些基本项目。

    在 Python class 对象中有两个“特殊”的初始化名称,一个是用户比较少担心的,称为 __new__,另一个是更常见的,称为 __init__

    当您调用类对象构造函数时,例如(基于您的示例)x = Game(args),这首先调用Game.__new__ 来获取用于保存对象的内存,然后Game.__init__ 来填充该内存。大多数情况下,您可以允许底层object.__new__ 分配内存,而您只需要填充它。(您可以使用自己的分配器来处理特殊的奇怪罕见情况,例如永远不会改变并且可能共享身份的对象,例如普通整数的方式。它也适用于做奇怪事情的“元类”。但这都是以后的话题。)

    您的Game.__init__ 函数在调用时使用“构造函数的所有参数”加上一个隐藏在前面的参数,即为该对象本身分配的内存。 (对于主要是“属性”字典的“普通”对象,加上类的魔术粘合剂,但对于具有__slots__ 的对象,属性字典被省略。)命名第一个参数self 只是一个约定——但不要不要违反它,如果你这样做,人们会讨厌你。 :-)

    没有什么需要您将所有参数保存到构造函数。您可以设置任何或所有您喜欢的实例属性:

    class Weird(object):
        def __init__(self, required_arg1, required_arg2, optional_arg3 = 'spam'):
            self.irrelevant = False
        def __str__(self):
            ...
    

    问题是 Weird() 实例在初始化后几乎没用,因为您需要传递两个被简单丢弃的参数,并给出第三个也被丢弃的可选参数:

    x = Weird(42, 0.0, 'maybe')
    

    需要这些丢弃的参数的唯一一点是为了将来的扩展,因为它是(在早期开发期间你可能有这些未使用的字段)。因此,如果您没有立即使用和/或将参数保存到 __init__,那么在 Weird 中肯定有些奇怪。

    顺便说一句,在类定义中使用(object) 的唯一原因是向 Python 2.x 表明这是一个“新样式”类(与非常古老的 Python“仅实例”类不同) .但通常最好使用它——它使我上面所说的 object.__new__ 成为真的,例如 :-)——直到 Python 3,旧式的东西完全消失了。

    【讨论】:

      【解决方案3】:

      参数名称应该有意义,以传达它们在函数/方法中所扮演的角色或有关其内容的一些信息。

      您可以看到构造函数的参数更加重要,因为它们通常是新实例的工作所必需的,并且包含类的其他方法所需的信息。

      假设您有一个接受playerListGame 类。

      class Game:
      
          def __init__(self, playerList):
              self.playerList = playerList    # or self.players = playerList
      
          def printPlayerList(self):
              print self.playerList           # or print self.players
      

      类的各种方法都需要这个列表。因此将其分配给self.playerList 是有意义的。您也可以将其分配给self.players,无论您觉得更舒服并且 认为是可以理解的。但是如果你不将它分配给self.<somename>,它就无法在其他方法中访问。

      因此,如何命名参数/属性/等(there are some special class methods though)并没有什么特别之处,但是使用有意义的名称会使代码更容易理解。或者如果你有的话,你会理解上述类的含义吗:

      class G:
      
          def __init__(self, x):
              self.y = x
      
          def ppl(self):
              print self.y
      

      ? :) 完全一样,但更难理解...

      【讨论】:

        猜你喜欢
        • 2016-12-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-09
        • 2017-12-09
        • 1970-01-01
        • 2020-06-16
        • 2023-03-22
        相关资源
        最近更新 更多