关于python:类变量和实例变量之间的区别

Difference between Class variables and Instance variables

我已经阅读了很多关于栈交换的答案,比如python——为什么在一个类中使用"self"?在阅读了这些之后,我了解到实例变量对于类的每个实例都是唯一的,而类变量则在所有实例之间共享。在玩的时候我发现了这个密码-

1
2
3
4
5
6
7
8
9
class A:
    x = []
    def add(self):
        self.x.append(1)

x = A()
y = A()
x.add()
print"Y's x:",y.x

是否给出输出[1]。但是,此代码-

1
2
3
4
5
6
7
8
9
10
class A:
    x = 10
    def add(self):
        self.x += 1

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

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

当我认为应该是11时,给出输出为10。如果这是一个非常平常的问题,请原谅我,但我在编程方面不是很有经验。


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

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

1
self.x += 1

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

1
self.x = self.x + 1

python所做的是:

  • 查找self.x。此时,self没有实例属性x,因此找到类属性A.x,值为10
  • 对RHS进行评估,得出结果11
  • 将此结果分配给self的新实例属性x
  • 所以在下面,当您查找x.x时,您会得到这个在add()中创建的新实例属性。在查找y.x时,仍然会得到class属性。要更改class属性,您必须显式地使用A.x += 1–查找仅在读取属性值时发生。

    第一个例子是一个经典的gotcha,这也是您不应该使用类属性作为实例属性的"默认"值的原因。当你打电话:

    1
    self.x.append(1)

    没有对self.x的分配。(改变可变对象的内容,如list,与赋值不同。)因此,没有新的实例属性添加到x,它将隐藏它,并且查找x.xy.x,稍后从class属性中给出相同的列表。

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

    (我从这个答案中提取了以上内容,请参阅此处了解更多详细信息。)