【问题标题】:Static class variables and `self` in PythonPython中的静态类变量和`self`
【发布时间】:2014-03-27 19:44:31
【问题描述】:

为什么下面的例子表现不同?

示例 1:foo 的行为似乎类似于特定于各种对象的类变量

class A: 
    foo = 1
a, b = A(), A()
a.foo = 5
print b.foo
----------------
Output: 1

示例 2:foo 似乎表现得像一个对所有对象都相同的静态类变量。也许这种行为与作为指针工作的列表有关。

class A: 
    foo = []
a, b = A(), A()
a.foo.append(5)
print b.foo
----------------
Output: [5]

示例 3:不起作用

class A: 
    self.foo = []
a, b = A(), A()
a.foo.append(5)
print b.foo
----------------
Output: Error

【问题讨论】:

    标签: python class oop static self


    【解决方案1】:

    前两个例子都是类属性。它们看起来不同的原因是因为您在两种情况下都没有做同样的事情:您在第一种情况下分配一个新值并在第二种情况下修改现有值。

    请注意,您在前两个示例中没有做同样的事情。在第一个示例中,您执行a.foo = 5,分配一个新值。在第二个示例中,如果您执行类似的操作,分配a.foo = [5],您将看到与第一个示例相同的结果。但是您使用a.foo.append(5) 更改了现有列表,因此行为不同。 a.foo = 5 仅更改 变量(即它指向的值); a.foo.append(5) 改变 本身。

    (请注意,在第一个示例中没有办法与第二个示例等效。也就是说,没有什么像 a.foo.add(1) 那样将 1 加到 5。那是因为整数不是可变的,但列表是可变的。但是什么重要的不是列表“是”可变的,而是你改变了一个。换句话说,你可以对列表做什么并不重要,重要的是你在特定代码中实际做了什么.)

    另外,请注意虽然您在类定义中定义的foo 是一个类属性,但是当您执行a.foo = 5 时,您正在实例 上创建一个新属性。它恰好与类属性同名,但并没有改变类属性的值,这是b.foo 仍然看到的。

    最后一个示例不起作用,因为就像前两个示例一样,class 块内的代码位于类范围内。没有self,因为在定义类时还没有实例。

    在 StackOverflow 上有很多很多关于此的其他问题,我敦促您搜索并阅读其中的一些问题,以便更全面地了解其工作原理。

    【讨论】:

    • 我了解示例 3 和 selfself 作为我们定义的类的实际对象工作,因此此时没有任何对象。但是,我不明白前两个示例之间的区别。我显然可以更改整数的内容,因为在第一个示例中 print a.foo = 5 。你能再澄清一点吗?
    • @Konstantin:这并没有改变整数 value,它改变了 variable 指向的内容。请参阅this page,了解 Python 中哪些是和不是哪些变量的简单说明。
    • "它们看起来不同的原因是您可以更改列表的内容,但不能更改整数的内容。"不,它们不同的原因在于 1) 涉及重新分配和 2) 就地更改(诚然,只有可变对象才有可能)。您在下一段中说明了这一点,但第一个可能有点误导。
    • @glglgl:你是对的,这实际上也是我的一个小问题。我编辑了答案以使其更清楚。
    • @BrenBarn 现在,绝对完美。 (除非你可以压缩第 1 和第 2 段。)如果可以的话,我会再给你一个 +1 :-)
    【解决方案2】:

    这不起作用:

    class A: 
        self.foo = []
    

    这会引发错误。

    NameError: name 'self' is not defined
    

    因为self在Python中不是关键字,它只是一个通常分配给类实例的变量名,在调用类时传递给类的方法。

    这是一个例子:

    class A(object): 
        def __init__(self):
            self.foo = []
    
    a, b = A(), A()
    a.foo.append(5)
    print(b.foo)
    

    然后返回:

    []
    

    每个初始化时,都会得到自己的列表,可以通过属性foo访问,当一个被修改时,另一个作为单独的列表存储在内存中的不同位置,不受影响.

    【讨论】:

    • 亚伦,非常好的解释!自从我开始使用 Python 而不是 C/C++ 做一些事情后,有点挣扎
    • @Konstantin 感谢您的认可。 :) 我打算让布伦去做,但我想我有一些东西可以补充他。
    【解决方案3】:

    区别与可变性/不变性无关,而与执行的操作有关。

    在示例 1 中,该类具有属性 foo。创建对象后,您为对象赋予另一个属性foo,它会影响前一个属性。所以类属性充当一种“默认”或“后备”。

    在示例 2 中,您有一个要对其执行操作的对象(诚然,该对象仅适用于可变对象)。所以A.foo引用的对象,由于缺少同名的实例属性,也可以通过a.foob.foo访问,添加了5

    示例 3 不起作用,因为 self 在您使用它的地方不存在。

    请注意,示例 1 也适用于可变对象,例如列表:

    class A: 
        foo = []
    a, b = A(), A()
    a.foo = []
    a.foo.append(5)
    b.foo.append(10)
    print a.foo # [5]
    print b.foo # [10]
    print A.foo # [10]
    

    这里a.foo 得到一个新的空列表。 b.foo,缺少实例属性,继续引用类属性。所以我们有两个相互独立的空列表,正如我们在.append()ing 时看到的那样。

    【讨论】:

    • 示例 1 中关于“fallback”属性的重要一点,因此它就像类的静态成员一样,同时作为实例的默认属性。当您将其更改为 A.foo = 7 等类的静态变量时,默认属性会更改
    猜你喜欢
    • 2018-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-12
    • 2013-09-08
    • 1970-01-01
    相关资源
    最近更新 更多