Python变量的作用域
Python程序有各种各样的命名空间,它指的是在该程序段内一个特定的名称是独一无二的,它和其它同名的命名空间是无关的。
在Python中每一个函数都有自己的命名空间,如果在函数体外部,也就是主程序范围内定义一个变量a,然后在函数体内也定义一个变量a,那么两者指带的是不同的变量。但是要知道的是场景总是多变的,假设我需要在函数体内也访问并修改一个定义在主程序范围内的变量是否可以呢?
先来看一个例子,我们在函数体外定义了一个变量name
,我们在函数体内使用到了这个变量,最终在执行函数体时也可以正确获取到定义在函数体外的变量值。
name = 'james'
def print_username():
print('Hello:', name)
print_username()
# 输出如下:
# Hello: james
那么,假设我需要在函数体内也访问并修改一个定义在主程序范围内的变量是否可以呢?
name = 'james'
def print_username():
if name == 'james':
name = 'mary'
print('Hello:', name)
print_username()
# 输出如下:
# UnboundLocalError: local variable 'name' referenced before assignment
没错,你看到了它会报错。但是如果我在函数体内重新定义一个和外部区域一样的变量名,就不会报错了。
name = 'james'
def print_username():
name = 'james'
if name == 'james':
name = 'mary'
print('Hello:', name)
print_username()
# 输出如下:
# Hello: mary
但是,别真以为是这样,我们在外面单独输出name
变量看看它的值是否被修改了。你可以从下面的代码输出结果发现变量的值依然是james。这发生了什么呢?一起来看看,我们在第一行代码处定义了变量name
并赋值为james。然后在函数体内也定义了相同的变量和变量值,但是实际上,这里的变量是另一个变量,因为我们在一开始就说过Python中的每个函数体有它自己的命名空间,所以这两个变量生存在各自的命名空间内互相没任何关系。
name = 'james'
def print_username():
name = 'james'
if name == 'james':
name = 'mary'
print('Hello:', name)
print_username()
print(name)
# 输出如下:
# Hello: mary
# james
我们可以用Python的内置函数id()来查看对象的内存空间一查究竟。
name = 'james'
def print_username():
name = 'james'
if name == 'james':
name = 'mary'
print('Hello:', name)
print("inside:",id(name))
print_username()
print("outside:", name)
print("outside:",id(name))
# 输出如下:
# Hello: mary
# inside: 4691515968
# outside: james
# outside: 4898181448
从对象的唯一ID值我们就可以证明这两个变量其实没有任何关系。
那么问题来了,如果我就是想在函数体内修改函数体外的全局变量名的数值该怎么做呢?在Python中,只需要在你要调用的函数体内的变量名前显式地添加关键字global就可以了。再次执行一遍代码,我们可以看到两个变量name
的各自对象ID值都相同了,而且输出的值也被修改为mary了,说明函数体内的变量成功修改了全局变量name,它们已经是同一个对象了。
name = 'james'
def print_username():
global name
if name == 'james':
name = 'mary'
print('Hello:', name)
print("inside:",id(name))
print_username()
print("outside:", name)
print("outside:",id(name))
# 输出如下:
# Hello: mary
# inside: 4691515968
# outside: mary
# outside: 4691515968
要记住,如果函数中不声明关键字global,那么python会使用局部命名空间,同时变量也是局部的。
另外,Python还提供了两个内置函数可以帮助我们获取命名空间内的信息。
- locals() 以字典的形式返回一个局部命名空间的内容
- globals() 以字典的形式返回一个全局命名空间的内容
我们可以看到locals()函数输出了局部命名空间里的内容,只含有局部变量name
,因为我们没有使用gloabl关键字将它全局化。而全局命名空间显示的内容就多了很多。
name = 'james'
def print_username():
name = 'james'
if name == 'james':
name = 'mary'
print('locals:', locals())
print_username()
print('globals:', globals())
# 输出如下:
# locals: {'name': 'mary'}
# globals: {'__name__': '__main__', '__doc__': 'Automatically created module for IPython # interactive environment', '__package__': None, '__loader__': None, '__spec__': None, #'name': 'james'}