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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
object t1: ['t1'] {}  
object t2: ['t1'] {}
```
从例子可以看到,这时的 content 就相当于一个static 变量,被所有实例共享。**注意后面那个花括号{}表示实例的所有对象属性,只是当前实例没有自己的属性**。



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

```py
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__
```
输出的结果如下所示:
```
object t1: ['t1'] {'content':['t1']}
object t2: [] {'content':[]}

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
    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。这个可以从下面的例子看出,假如将例二的代码修改成如下所示:
```py
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
```
那输出结果是:
```
object t1: ['t1'] {'content':['t1']}
object t2: [] {'content':[]}
class TestAttribute: ['tt']
```
可以看到这三个对象的属性均独立。

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

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

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

```py
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__
```
输出结果:
```
object t1: 1 {'num': 1}
object t2: 0 {}

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