6

我已经在 Stack Exchange 上阅读了很多答案,比如Python - 为什么在课堂上使用“self”? 阅读这些答案后,我了解到实例变量对于类的每个实例都是唯一的,而类变量在所有实例之间共享。

在玩耍时,我发现这段代码给出了输出[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

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

4

1 回答 1

12

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

这意味着当您在第二个示例中执行以下操作时:

self.x += 1

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

self.x = self.x + 1

Python 所做的是:

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

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


你的第一个例子是一个经典的陷阱,你不应该使用类属性作为实例属性的“默认”值的原因。你打电话时:

self.x.append(1)

没有任务self.x发生。(更改可变对象的内容,如 a list,与赋值不同。)因此,没有新的实例属性添加到x它会影响它,查找x.xy.x稍后为您提供与类属性相同的列表。


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

(我从这个 SO 答案中提取了上述内容,有关更多详细信息,请参见此处。)

于 2013-11-03T14:25:17.333 回答