python 的域规则
- 变量作用域:在 Python 程序中创建,改变,查找变量名时,都是在一个保存变量名的空间中进行,我们称之为命名空间,也被称之为作用域.Python 的作用域是静态的,在源代码中变量名被赋值的位置决定了该变量能被访问的范围.即 Python 变量的作用域由变量所在源代码中的位置决定.不同的作用域可以存在相同的变量名.
例 1:
a = 30
def ex():
b = 25
return a+b
a 是全局变量,b 是局部变量;函数可以对 a,b 进行访问,代码的主体部分只能访问 a.
例 2:
globalVar = 100 #全局作用域
def test_scope():
enclosingVar = 200 #嵌套作用域
def func():
localVar = 300 #局部作用域
print __name__ #内置作用域
当搜索一个标识符时,python 会先从局部作用域开始查找,如果找不到则到上一层嵌套结构中 def 或 lambda 函数的嵌套作用域中继续寻找,之后是全局作用域,最后是内置作用域.按这个查找原则,在第一处找到的地方停止.如果还未找到,抛出异常 NameError.
搜索变量名的优先级:局部作用域 > 嵌套作用域 > 全局作用域 > 内置作用域
下例中 text 的作用域实在函数内部,在全局中是无法调用的.
>>> def func():
text = 'hello world'
>>> print(text)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'text' is not defined
作用域的类型
在 Python 中,使用一个变量时并不严格要求需要预先声明它,但是在真正使用它之前,它必须被绑定到某个内存对象 (被定义,赋值);这种变量名的绑定将在当前作用域中引入新的变量,同时屏蔽外层作用域中的同名变量.
局部作用域:
局部变量:包含在 def 关键字定义的语句块中,即在函数中定义的变量.每当函数被调用时都会创建一个新的局部作用域.Python 中也有递归,即自己调用自己,每次调用都会创建一个新的局部命名空间.在函数内部的变量声明,除非特别的声明为全局变量,否则均默认为局部变量.有些情况需要在函数内部定义全局变量,这时可以使用 global 关键字来声明变量的作用域为全局.局部变量域就像一个 栈,仅仅是暂时的存在,依赖创建该局部作用域的函数是否处于活动的状态.所以,一般建议尽量少定义全局变量,因为全局变量在模块文件运行的过程中会一直存在,占用内存空间.
注意:如果需要在函数内部对全局变量赋值,需要在函数内部通过 global 语句声明该变量为全局变量.
# 通过global语句在函数体中重新赋值全局变量
>>> global_str = '100'
>>> def func():
... global global_str
... global_str = '500'
... local_str = '200'
... return local_str + global_str
...
>>> func()
'200500'
>>> print(global_str) #全局变量被重新赋值了
500
#不使用global语句
>>> global_str = '100'
>>> def func():
... global_str = '500'
... local_str = '200'
... return local_str + global_str
...
>>> func()
'200500'
>>> print(global_str) #这里全局变量的值,没有改变
100
嵌套作用域:python 闭包函数
对一个函数而言,局部作用域是定义在此函数内部的局部作用域,而嵌套作用域是定义在此函数的上一层父级函数的局部作用域.主要是为了实现 Python 的闭包,而增加的实现.
variable = 100
def test_scopt():
variable = 200
print variable
def func():
print variable #这里的变量variable在嵌套作用域中绑定了内存对象200,为函数func()引入了一个新的变量
func()
test_scopt()
print variable
运行结果为:
200 # 嵌套作用域print的值
200 # func()函数局部作用域无法找到variable的值,向父级test_scopt()寻找
100 # 无法访问局部作用域,打印全局变量的值
全局作用域
即在模块层次中定义的变量,每一个模块都是一个全局作用域.也就是说,在模块文件顶层声明的变量具有全局作用域,从外部开来,模块的全局变量就是一个模块对象的属性.
注意:全局作用域的作用范围仅限于单个模块文件内.
内置作用域
系统内固定模块里定义的变量,如预定义在 builtin 模块内的变量.
对变量的修改
一个非局部变量相对于局部变量而言,默认是只读而不能修改的.如果希望在局部变量中修改定义在非局部变量的变量,为其绑定一个新的值,Python 会认为是在当前的局部变量中引入一个新的变量 (即便内外两个变量重名,但却有着不同的意义).即在当前的局部变量中,如果直接使用非局部变量中的变量,那么这个变量是只读的,不能被修改,否则会在局部变量中引入一个同名的新变量.
可以通过 nonlocal 和 global 等关键字修改非局部变量的值
# 直接修改实质是定义了一个新的变量
>>> global_str = '100'
>>> def func():
... global_str = '500' #引入一个新变量,覆盖全局变量中的变量
... local_str = '200'
... return local_str + global_str
...
>>> func()
'200500'
>>> print(global_str) #这里全局变量的值,没有改变
100
global 语句
num = 100
def test():
def nested():
global num # 在局部作用域中修改全局变量的值
print('current=', num)
num = 520
return nested
test()()
print(num)
输出结果:
current= 100
520
注意:test()() 表示会自动调用函数 test() 的返回值,且此返回值必须为可调用类型,即存在__ call__方法.返回一个函数,所以也会执行返回的函数体代码.
nonlocal 关键字(python3.x 新添加的关键字)
def outer():
count = 10
def inner():
nonlocal count
count = 20
print(count)
inner()
print(count)
outer()
输出结果:
20
20
命名空间和作用域
命名空间:是一个包含了该空间中所有变量和变量值的字典,不同的命名空间之间是相互隔离的,所以在不同的命名空间可以创建同名变量,通过句点标识符来调用和区别,例如:student.name 和 teacher.name 是两个不同的命名空间.Python 内置了两个查询命名空间的字典的内置函数:globals(),locals():
# 在全局环境中调用,所以locals()和globals()返回值一致.
>>> globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>}
>>> locals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>}
注意:
根据调用地方的不同,globals() 和 locals() 函数可被用来返回全局和局部命名空间里的名字.
如果在函数内部调用 locals(),返回的是所有能在该函数里访问的命名.
如果在函数内部调用 globals(),返回的是所有在该函数里能访问的全局名字.
两个函数的返回类型都是字典.所以能用 keys() 函数摘取变量名.
作用域:是一个变量能够有效的区域,全局作用域的全局变量在整个模块中有效,局部作用域中的局部变量只在类或函数中有效.创建一个作用域会同时生成一个命名空间,并且作用域包围了其命名空间.作用域是为了实现变量查询的路径,就如上文所述,如何局部作用域中含有于全局作用域同名的变量时,局部作用域会屏蔽掉全局作用域,这是因为变量的查询路径中,局部作用域要先于全局作用域,然后再到相对的命名空间中获取变量的值.
来源: http://www.jianshu.com/p/f3006ebc8b12