类的作用域
在之前的文章中介绍过Python的作用,在这个小结介绍下类的作用域
In [1]: class E:
...: NAME = 'E' # 类的直接下级作用域,叫做类变量
...: def __init__(self, name):
...: self.name = name # 关联到实例的变量,叫做实例变量
...:
In [2]: e = E('e')
In [4]: E.NAME
Out[4]: 'E'
In [5]: e.NAME
Out[5]: 'E'
上面的例子可以看出,类变量对类和对象都是可见的
In [6]: E.name # 直接使用类名来访问实例变量的时候报错了,说明类是不能直接访问实例变量的
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-6-b6daf181be33> in <module>()
----> 1 E.name
AttributeError: type object 'E' has no attribute 'name'
In [7]: e.name # 实例变量可以使用实例对象来进行访问和调用
Out[7]: 'e'
所有实例共享类变量,例如:
In [8]: e2 = E('e2') # 重新定义一个实例对象
In [11]: e.NAME # 使用之前的实例来进行访问类变量
Out[11]: 'E'
In [9]: e2.NAME # 使用刚定义的实例访问类变量,结果是一样的
Out[9]: 'E'
Python可以动态的给对象增减属性
上面说了所实例共享类变量,但是下面的例子又反驳了这个说法
In [12]: e2.NAME = 'E2' # 给实例e2的NAME属性赋值
In [13]: e2.NAME # 获取e2的属性值
Out[13]: 'E2'
In [14]: e.NAME # 获取e的属性
Out[14]: 'E'
In [15]: e.xxx = 3 # 给e对象添加一个xxx的属性
In [16]: e2.xxx # 使用e2访问xxx属性报出了错误
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-16-8ca2718f6555> in <module>()
----> 1 e2.xxx
AttributeError: 'E' object has no attribute 'xxx'
发现e2和e的实例对应的类变量的值是不相同,而且e新创建的属性是无法访问的
从上面的例子可以看出,当给实的类变量赋值的时候,就相当于动态的给这个实例增加了一个属性,覆盖了类变量
In [17]: E.NAME = 'LANYULEI' # 直接使用类名来重新定义NAME的属性
In [18]: e.NAME # 使用e获取NAME的值
Out[18]: 'LANYULEI'
In [19]: e2.NAME # 使用e2来获取NAME的值,得到的是不一样的结果
Out[19]: 'E2'
这样就能看出,e因为么有重新给类变量赋值,因此当类变量修改的时候,e实例也就修改了,因为e2修改过NAME属性的值,相当于动态的给e2实例添加了一个变量,因此当类变量重新赋值的时候,e2是没有变化的
对于类变量和实例的准则其实就是,赋值及创建。
属性的查找顺序
上面的例子中看出了,类变量的赋值及创建,当实例重新定义类变量的时候,就是给实例动态的添加了属性,原理是什么呢?
In [21]: e.__class__.NAME
Out[21]: 'LANYULEI'
In [23]: E.__dict__['NAME']
Out[23]: 'LANYULEI'
在实例中会有两个默认属性分别是__dict__和__class__, 当你给实例动态的添加属性的时候,那么就是给__dict__中追加数据的,
属性的查找顺序
- dict # 先从__dict_找有没有这个属性
- class # 实例类的一个引用
另外类也是一个对象,因此也是可以动态的添加属性的