这篇文章介绍 Python 的名称空间以及变量的作用域。
Python 的名称(Name)是对象的一个标识(Identifier)。我们知道,在 Python 里面一切皆对象,名称就是用来引用对象的。说得有点玄乎,我们以例子说明。
例如,在a = 2
这个语句中,2
是个存储在内存中的一个对象,名称a
则会引用2
这个对象,“引用”的含义是指可以通过名称a
来使用2
这个对象。我们可以使用id()
函数来获取对象的地址。
a = 2
print(id(2))
print(id(a))
输出:
43547988
43547988
可以看到,两都均指向同一个对象。我们再来看复杂一点的代码。
a = 2
print 'id(a) = %s' % id(a)
a = a + 1
print 'id(a) = %s' % id(a)
print 'id(3) = %s' % id(3)
b = 2
print 'id(2) = %s' % id(2)
print 'id(b) = %s' % id(b)
输出:
id(a) = 55606612
id(a) = 55606600
id(3) = 55606600
id(2) = 55606612
id(b) = 55606612
上述的代码发生了什么呢?我们以图1来说明。
起初,名称a
引用对象2
;
然后,执行操作a = a + 1
,这时对象3
被创建,名称a
引用对象3
,所以id(a)
和id(3)
输出相同的地址;
最后,执行b = 2
,名称b
引用对象2
,所以id(2)
和id(b)
输出相同的地址
这个例子也展示了 Python 在执行变量的赋值时,并不会重复创建一个对象的事实。名称采用动态绑定的机制使得 Python 更加高效,同一个名称可以引用不同类型的对象。
a = 5
a = 'hello world'
a = [1, 2, 3]
可以看到,a
先后引用了数字,字符串,列表的类型的对象,这在 Python 中完全是合法的。
了解 Python 的名称后,我们来看 Python 的名称空间。
名称空间是名称到对象的映射。
在 Python 中,名称空间采用字典来实现。Python 的名称空间包括:
abs()
不同的名称空间内的名称不会相互冲突,即是它们采用相同的名称。这也正是名称空间的作用。
内置名称空间在 Python 解释器启动时就创建了,直到 Python 解释器退出时内置名称空间才失效。这使得我们可以在程序的任何位置使用内置名称空间内的名称,例如,id()
,print()
等函数。
模块名称空间当模块被引用时创建,直到 Python 解释器退出时模块名称空间才失效。
函数名称空间在函数被调用时创建,函数返回后失效。
Python 的作用域(scope)决定了我们在程序中能否直接使用名称空间中的名称,直接访问的意思是指不需要在名称前添加名称空间的前缀。对于 Python 来说,至少存在以下三类的作用域。
当在函数内部使用一个名称时,为了查找出该名称所引用的对象,Python 解释器先在函数名称空间查找,接着在模块名称空间查找,最后在内置名称空间查找,直到寻找到该名称为止。
当函数 A 处于函数 B 的内部时,函数 A 的作用域处于函数 B 作用域之内。
def outer_function():
b = 20
def inner_func():
c = 30
a = 10
在这个例子中,名称a
在全局名称空间中,名称b
在函数outer_function
的局部名称空间,名称c
则在函数inner_func
的局部名称空间。
当我们在函数inner_func
时,c
是个局部的名称,b
是个非局部的名称,而a
则是个全局的名称。在函数inner_func
中,我们可以对c
进行读取操作和赋值操作,而只能对b
和a
进行读取操作。当对b
进行赋值时,一个新的名称将会被创建,这个新的名称处于inner_func
函数局部名称空间中。对a
进行赋值时也会在局部名称空间中创建一个新的名称。
我们使用以下的例子来说明。
def outer_function():
a = 20
def inner_function():
a = 30
print('a = %s' % a)
inner_function()
print('a = %s' % a)
a = 10
outer_function()
print('a = %s' % a)
输出:
a = 30
a = 20
a = 10
在函数inner_function
中,我们对a
进行了赋值操作,但函数outer_function
中的a
仍然为20
,全局名称空间中的a
则仍然为10
。
为了在函数作用域中对全局的名称进行读取或者赋值操作,需要将这个名称声明为global
。
def outer_function():
global a
a = 20
def inner_function():
global a
a = 30
print('a = %s' % a)
inner_function()
print('a = %s' % a)
a = 10
outer_function()
print('a = %s' % a)
输出:
a = 30
a = 30
a = 30
可以看到,通过global
,我们在不同的作用域对全局名称a
进行了赋值操作,最后在函数inner_function
中对a
的赋值也就是全局名称a
的值。