【问题标题】:Difference between Class variables and Instance variables类变量和实例变量之间的区别
【发布时间】:2013-11-03 14:14:10
【问题描述】:

我已经在 Stack Exchange 上阅读了很多答案,例如 Python - why use "self" in a class? 阅读这些答案后,我了解到实例变量对于类的每个实例都是唯一的,而类变量在所有实例之间共享。

在玩的时候,我发现这段代码给出了输出[1]

class A:
    x = []
    def add(self):
        self.x.append(1)

x = A()
y = A()
x.add()

print "Y's x: ", y.x

但是,这段代码给出了10 作为输出,而我认为它应该是11

class A:
    x = 10
    def add(self):
        self.x += 1

x = A()
y = A()
x.add()

print "Y's x: ", y.x

为什么当我运行x.add()A 类变量没有更新?我对编程不是很有经验,所以请见谅。

【问题讨论】:

  • 谢谢@mbatchkarov,我不知道我是怎么漏掉的 :)

标签: python class oop


【解决方案1】:

类变量被实例属性隐藏。这意味着在查找属性时,Python 首先查找实例,然后查找类。此外,在对象上设置变量(例如self)总是会创建一个实例变量——它永远不会改变类变量。

这意味着,在您的第二个示例中,当您这样做时:

self.x += 1

这(在这种情况下,见脚注)相当于:

self.x = self.x + 1

Python 所做的是:

  1. 查找self.x。此时self没有实例属性x,所以找到了类属性A.x,其值为10
  2. 评估 RHS,给出结果11
  3. 此结果分配给self 的新实例属性x

因此,当您在下面查找x.x 时,您会看到在add() 中创建的这个新实例属性。在查找y.x 时,您仍然会获得类属性。要更改类属性,您必须显式使用A.x += 1 - 仅在读取属性值时才会进行查找。


您的 first 示例是一个经典的陷阱,也是您不应将类属性用作实例属性的“默认”值的原因。当你打电话时:

self.x.append(1)

没有分配给self.x。 (更改可变对象的内容,例如list,与赋值不同。)因此,不会向x 添加新的实例属性来影响它,并查找x.xy.x 稍后会从类属性中为您提供相同的列表。


注意:在 Python 中,x += y 并不总是等同于 x = x + y。 Python 允许您将就地运算符与类型的普通运算符分开覆盖。这对于 mutable 对象很有意义,其中就地版本将直接更改内容而不重新分配表达式的 LHS。但是,不可变 对象(例如第二个示例中的数字)不会覆盖就地运算符。在这种情况下,该语句确实会被评估为常规添加和重新分配,从而解释您看到的行为。

(我从this SO answer 中提取了上述内容,请参阅此处了解更多详细信息。)

【讨论】:

  • 实际上,x += ... 并不总是等同于x = x + ...。您可以观察到,如果您使用列表类变量更改代码以使用 self.x += [1] 而不是 self.x.append(1)
  • 我有点怀疑x += y 通常等同于x = x + y__iadd____add__ 是不同的。
  • @DSM 嗯。它似乎适用于这个例子,我会在文档中戳一下,看看我是否可以在不挥手的情况下表达它。编辑:多尼佐。
  • 什么意思你不应该使用类属性作为实例属性的“默认”值?
  • @Mayank 这是一种有点狡猾的模式,您可以使用它们来“初始化”实例属性,而不是将该代码放入__init__()。它适用于不可变值,但如果您最终在实例之间共享可变值,则会出现问题。如果您打算将类变量 x 真正用作初始化 self.x 的一种方式,这基本上就是您在第二个示例中所做的事情。
猜你喜欢
  • 2017-02-15
  • 1970-01-01
  • 1970-01-01
  • 2020-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-11
  • 2019-05-19
相关资源
最近更新 更多