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。如果这是一个非常平常的问题,请原谅我,但我在编程方面不是很有经验。
- 谢谢@mbatchkarov,我不知道我是怎么让它溜走的:)
类变量由实例属性隐藏。这意味着在查找属性时,Python首先在实例中查找,然后在类中查找。此外,在一个对象上设置一个变量(例如self总是创建一个实例变量——它从不更改类变量。
这意味着,在第二个示例中,当您执行以下操作时:
在这种情况下,见脚注,相当于:
python所做的是:
查找self.x。此时,self没有实例属性x,因此找到类属性A.x,值为10。
对RHS进行评估,得出结果11。
将此结果分配给self的新实例属性x。
所以在下面,当您查找x.x时,您会得到这个在add()中创建的新实例属性。在查找y.x时,仍然会得到class属性。要更改class属性,您必须显式地使用A.x += 1–查找仅在读取属性值时发生。
第一个例子是一个经典的gotcha,这也是您不应该使用类属性作为实例属性的"默认"值的原因。当你打电话:
没有对self.x的分配。(改变可变对象的内容,如list,与赋值不同。)因此,没有新的实例属性添加到x,它将隐藏它,并且查找x.x和y.x,稍后从class属性中给出相同的列表。
注:在python中,x += y并不总是等同于x = x + y。python允许您分别重写类型的就地运算符和普通运算符。对于可变对象来说,这基本上是有意义的,其中就地版本将直接更改内容,而不重新分配表达式的lhs。但是,不可变对象(如第二个示例中的数字)不会重写就地运算符。在这种情况下,语句会被评估为常规的添加和重新分配,从而解释您看到的行为。
(我从这个答案中提取了以上内容,请参阅此处了解更多详细信息。)
- 很好的解释!
- 实际上,x += ...并不总是等同于x = x + ...。您可以观察到,如果使用列表类variabel更改代码以使用self.x += [1]而不是self.x.append(1)。
- 我想说一点关于x += y一般等同于x = x + y;__iadd__和__add__是不同的。
- @dsm-hm.对于这个例子来说,它似乎是有效的,但是我会戳一下文档,看看我是否可以在不挥手的情况下将其表达出来。编辑:多尼索。
- 什么意思是不应该使用类属性作为实例属性的"默认"值?
- @也许这是一种有点狡猾的模式,在这种模式中,您使用它们来"初始化"实例属性,而不是将代码放入__init__()中。它适用于不可变的值,但如果最终在实例之间共享可变的值,则会有问题。如果您希望类变量x真正成为初始化self.x的一种方法,那么这基本上就是您在第二个示例中所做的。
- 非常感谢您提供详细的答案!