一个类实例化后,实例是一个对象,有属性。同样,类也是一个对象,它也有属性。定义一个类属性,如下:
class A:
x = 7
定义一个很简单的类,类中有一个变量x=7
,在类A中,变量x所引用的数据,能够直接通过类来调用。或者说x是类A的属性,这种属性称为类属性。
class A:
x = 7
if __name__ == '__main__':
foo = A()
print(foo.x)
实例化,通过实例也可以得到这个属性,这个属性叫做“实例属性”。
class A:
x = 7
if __name__ == '__main__':
foo = A()
print(foo.x)
foo.x += 1
print(foo.x)
print(A.x)
实例属性更新了,类属性没有改变。这至少说明,类属性不会被实例属性左右,也可以进一步说“类属性与实例属性无关”。那么foo.x+=1
的本质是什么呢?其本质是该实例foo又建立了一个新的属性,但是这个属性(新的foo.x)居然与原来的属性(旧的foo.x)重名,所以,原来的foo.x就被“遮盖了”,只是访问到新的foo.x,它的值是8。
class A:
x = 7
if __name__ == '__main__':
foo = A()
print(foo.x)
foo.x += 1
del foo.x
print(foo.x)
既然新的foo.x“遮盖”了旧的foo.x,如果删除它,旧的不久就显现出来了?的确是。删除之后,foo.x就还是原来的值。实例属性跟着类属性而改变。
以上所言,是指当类中变量引用的是不可变数据。如果类中变量引用可变数据,情形会有所不同。因为可变数据能够进行原地修改。
class B:
y = [1, 2, 3]
if __name__ == '__main__':
print(B.y)
bar = B()
print(bar.y)
bar.y.append(4)
print(bar.y)
print(B.y)
运行结果:
[1, 2, 3]
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3, 4]
Process finished with exit code 0
从上面的比较操作中可以看出,当类中变量引用的是可变对象时,类属性和实例属性都能直接修改这个对象,从而影响另一方的值。
类属性不受实例属性左右。在类确定或者实例化之后,也可以增加和修改属性,其方法是通过类或者实例的点号操作来实现,即object.attribute
,可以实现对属性的修改和增加。
总结
- 实例属性跟着类属性而改变,类属性不会被实例属性左右。
- 当类中变量引用的是可变对象时,类属性和实例属性都能直接修改这个对象,从而影响另一方的值。
- 实例属性属于各个实例所有,互不干扰。
- 类属性属于类所有,所有实例共享一个属性。
- 在编写程序的时候,千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。