python中的类属性和实例属性

面向对象语言中,一般会有“静态变量”,也就是给整个类共享的变量,如C++,java中static修饰的变量。但是在 python 中并没有 static 这个关键字,实现类似功能需要依靠python中的类属性和实例属性的语法特点。本文主要就是讲述这两种属性的区别。

在讲述之前,需要清楚下面两个事实: 1)python 中类创建的对象叫实例 2)类和实例均是对象,均有自己的对象属性,可通过__dict__查看

下面先看一个例子:

1
2
3
4
5
6
7
8
9
10
11
class TestAttribute:
content = []
def addContent(self,x):
self.content.append(x)

if __name__ == '__main__':
t1 = TestAttribute()
t2 = TestAttribute()
t1.addContent('t1')
print 'object t1:', t1.content, t1.__dict__
print 'object t2:', t2.content, t2.__dict__

输出结果:

1
2
object t1: ['t1'] {}
object t2: ['t1'] {}
从例子可以看到,这时的 content 就相当于一个static 变量,被所有实例共享。注意后面那个花括号{}表示实例的所有对象属性,只是当前实例没有自己的属性

那假如各个实例要有自己独立的变量的?也很简单,**只需要在类的构造函数(也就是__init__函数)为变量赋值即可.**

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class TestAttribute:
content = []

def __init__(self):
self.content=[]

def addContent(self,x):
self.content.append(x)

if __name__ == '__main__':
t1 = TestAttribute()
t2 = TestAttribute()
t1.addContent('t1')
print 'object t1:', t1.content ,t1.__dict__
print 'object t2:', t2.content ,t2.__dict__

输出的结果如下所示:

1
2
object t1: ['t1'] {'content':['t1']}
object t2: [] {'content':[]}

从结果可知,现在的t1,t2的变量独立了。那原因是什么呢?

原因是Python中对象属性的获取是按照从下到上的顺序来查找属性

怎么理解上面这句话呢?以上面代码为例,类和实例的关系如下所示:

1
2
3
4
 TestAttribute
____|____
| |
t1 t2
输出t1.content时,python 解析器会先查看对象 t1 中是否有content这个属性,有的话输出这个属性的值,没有的话就往上查找 TestAttribute 中是否有这个属性并输出。

在上面的例一中,因为t1和t2的属性均为空,所以输出 t1.content 和 t2.content时实际上是输出 TestAttribute 的属性。又因为 TestAttribute 的 content 属性被修改了 t1 修改了,所以最后输出的的 t1.content 和 t2.content 内容一致。

而在例二中,因为在构造函数中的为content复制的操作使得每个被创建的实例均有自己的content属性,所以 t1 修改 content 时查到自己有content的属性,就只会修改自己的 content。不影响t2 的 content 和 TestAttribute 的 content。这个可以从下面的例子看出,假如将例二的代码修改成如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class TestAttribute:
content = []

def __init__(self):
self.content=[]

def addContent(self,x):
self.content.append(x)

if __name__ == '__main__':
t1 = TestAttribute()
t2 = TestAttribute()
t1.addContent('t1')
TestAttribute.content.append('tt')
print 'object t1:', t1.content ,t1.__dict__
print 'object t2:', t2.content ,t2.__dict__
print 'class TestAttribute:', TestAttribute.content
那输出结果是:
1
2
3
object t1: ['t1'] {'content':['t1']}
object t2: [] {'content':[]}
class TestAttribute: ['tt']
可以看到这三个对象的属性均独立。

那么如何为一个实例添加属性呢?

答案是通过赋值号 = 给实例所需添加的属性赋值。 通过赋值号 = 给实例所需添加的属性赋值实际上是将这个属性指向了新的引用,也就是新的内存空间。

如在例一中没有通过赋值号为 content 赋值,所以这个属性并没有成为 t1 自己的属性,输出t1.__dict__ 为空。而在例二中的构造函数里面为每个实例的content均赋值,所以例二中的三个对象的content属性独立。通过下面的例子可以更深入说明这点:

1
2
3
4
5
6
7
8
9
10
class TestAttribute:   
num = 0

if __name__ == '__main__':
t1 = TestAttribute()
t2 = TestAttribute()

t1.num = 1
print 'object t1:', t1.num, t1.__dict__
print 'object t2:', t2.num, t2.__dict__

输出结果:

1
2
object t1: 1 {'num': 1}
object t2: 0 {}

最后,小结如下: 1.Python中的类和实例是两个完全独立的对象; 2.Python中属性的获取是按照从下到上的顺序来查找属性; 3.为实例添加属性的方法:通过赋值号 = 给实例所需添加的属性赋值