1,类属性和实例属性
一个类实例化后,实例是一个对象,它有属性。不要忘记,Python中的类也是一个对象,且也有属性。所以就有了“类属性”和“实例属性”两个属性。
>>> class A(object):
x =7
在交互模式下,定义一个很简单的类,类中有一个变量x=7
>>> A.x
7
在类A中,变量x所引用的对象,能够直接通过类调用。或者说x是类A的属性,这就是所谓的“
类属性”。
>>> a = A()
>>> a.x
7
将类A实例化,通过实例a也可以得到属性(a.x),这个属性叫作“
实例属性”。
对于同一属性,可以用类来访问(类属性),在一般情况下,也可以通过实例来访问同样的属性。
区别之处:
>>> a.x += 1
>>> a.x
8
>>> A.x
7
实例属性(a.x)更新了,类属性(A.x)没有改变。这至少说明,类属性不会被实例属性左右
a.x+=1的本质是实例a又建立了一个新的属性,但是这个属性(新的a.x)居然与原来的属性(旧的a.x)重名,所以,原来的a.x就被“遮盖了”,只能访问到新的a.x,它的值是8。
如果删除它,旧的就会显现出来。
>>> a.x
8
>>> del a.x
>>> a.x
7
还可以通过建立一个不与旧的实例属性重名的实例属性,理解上述过程。
>>> a.y = a.x + 1
>>> a.y
8
>>> a.x
7
实例属性不左右类属性,反过来,类属性能否影响实例属性:
>>> A.x
7
>>> A.x += 1
>>> A.x
8
>>> a.x
8
综上,“类属性不受实例属性影响,但实例属性受到类属性左右”,不过,这个结论是有条件的,前面例子中类内的变量应用的是
不可变对象(整数)。
>>> class B(object):
y = [1,2,3]
>>> B.y
[1, 2, 3]
>>> b = B()
>>> b.y
[1, 2, 3]
>>> b.y.append(4)
>>> b.y
[1, 2, 3, 4]
>>> B.y
[1, 2, 3, 4]
>>> B.y.append("hello")
>>> B.y
[1, 2, 3, 4, 'hello']
>>> b.y
[1, 2, 3, 4, 'hello']
从上面的比较操作中,能得出,当类中变量引用的是
可变对象时,类属性和实例属性都能直接修改这个对象,从而影响另一方的值。
2,命名空间
命名空间(namespaces),在研究类或者面向对象编程中,它常常被提到。我们在类的知识范畴中理解“类命名空间”——定义类时,所有位于class语句中的代码都在某个命名空间中执行,即类命名空间。
命名空间是从所定义的命名到对象的映射集合。
不同的命名空间可以同时存在,彼此相互独立互不干扰。
命名空间因为对象的不同也有所区别,可以分为如下几种。
(1)
内置命名空间(Built-in Namespaces):Python运行起来,它们就存在了。内置函数的命名空间都属于内置命名空间,所以,我们可以在任何程序中直接运行它们,比如前面的id(),不需要做什么操作,拿过来就能直接使用。
(2)
全局命名空间(Module:Global Namespaces):每个模块创建它自己所拥有的全局命名空间,不同模块的全局命名空间彼此独立,不同模块中相同名称的命名空间,也会因为模块的不同而不相互干扰。
(3)
本地命名空间(Function&Class:Local Namespaces):模块中有函数或者类,每个函数或者类所定义的命名空间就是本地命名空间。如果函数返回了结果或者抛出异常,则本地命名空间也结束了。
程序在查询上述三种命名空间的时候,按照从里到外的顺序,即:Local Namespaces(本地)→Global Namesspaces(全局)→Built-in Namesspaces(内置)。
print locals() :访问本地命名空间的方法
print globals():访问全局命名空间
3,作用域
作用域是指Python程序可以直接访问到的命名空间。“直接访问”在这里意味着访问命名空间中的命名时无须加入附加的修饰符。
程序也是按照搜索命名空间的顺序,搜索相应空间的能够访问到的作用域。
#! /usr/bin/env python
# coding:utf-8
def outer():
a = 1
def inner():
a = 2
print "inner,a=",a #a = 2
inner()
print "outer,a=",a #a = 1
a =3
outer()
print "a=",a #a = 3
运行结果:
>>>
inner,a= 2
outer,a= 1
a= 3
访问不同的作用域,获取该对象作用域的参数值