目录
- 作用域定义
- 作用域分类
- 查找变量规则
9)Python作用域
作用域定义
作用域 是命名空间可直接访问的 Python 程序的文本区域。 “可直接访问” 的意思是,对名称的非限定引用会在命名空间中查找名称。
作用域分类
作用域虽然是静态确定的,但会被动态使用。执行期间的任何时刻,都会有 3 或 4 个命名空间可被直接访问的嵌套作用域:
- (Local) 局部作用域:最内层作用域,包含局部名称,并首先在其中进行搜索
- (Enclosing) 闭包函数作用域:封闭函数的作用域,包含非局部名称和非全局名称,从最近的封闭作用域开始搜索
- (Global) 全局作用域:倒数第二个作用域,包含当前模块的全局名称
- (Built-in) 内置作用域:最外层的作用域,包含内置名称的命名空间,最后搜索
查找变量规则
- python 寻找变量时候会按照L-E-G-B的顺序去查找,如果找不到则报ValueError
- python变量作用域取决于其函数代码块在整体代码中的位置,而不是被调用的位置:
示例如下:
1| a = 1
2|
3| def func2():
4| print(a)
5|
6| def func1():
7| a = 10
8| func2()
9|
10| func1()
11| >>> 1
func2() 的调用位置是第8行,但是a依然获取的是 第1行中的 “a = 1”
如果将fun2()写成闭包,则如下示例:
1| a = 1
2|
3| def func1():
4| a = 10
5| def func2():
6| print(a)
7| func2()
8|
9| func1()
10| >>> 10
- 如果函数体再没有声明的情况下,直接对可以查找到的外部变量进行修改则会报错:UnboundLocalError:
a = 1
def func1():
a += 1
print(a)
func1()
因为python规定,函数内部要修改一个变量,那么这个变量必须是内部变量,或者使用全局变量global进行声明!
a = 1
def func1():
global a
a += 1
print(a)
func1()
- 全局变量 global & 非全局变量nonlocal
示例1 演示如何引用不同作用域及命名空间:
def scope_test():
def do_local():
spam = "local spam"
def do_nonlocal():
nonlocal spam
spam = "nonlocal spam"
def do_global():
global spam
spam = "global spam"
spam = "test spam"
do_local()
print("After local assignment:", spam)
do_nonlocal()
print("After nonlocal assignment:", spam)
do_global()
print("After global assignment:", spam)
scope_test()
print("In global scope:", spam)
>>>
After local assignment: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam
注意,局部 赋值(这是默认状态)不会改变 scope_test 对 spam 的绑定。 nonlocal 赋值会改变 scope_test 对 spam 的绑定,而 global 赋值会改变模块层级的绑定。
而且,global 赋值前没有 spam 的绑定。
如前所述,python寻找变量会按照 L - E - G - B的顺序进行查找,global的作用就是声明使用G层的变量。
想要使用E层的变量怎么办呢?可以使用nonlocal 进行声明,示例如下:
a = 1
print("函数outer调用之前全局变量a的内存地址: ",id(a))
def outer():
a = 2
print("函数outer调用之时闭包外部的变量a的内存地址: ",id(a))
def inner():
nonlocal a
a = 3
print("函数inner调用之后闭包内部变量a的内存地址: ",id(a))
inner()
print("函数inner调用之后,闭包外部的变量a的内存地址: ",id(a))
outer()
print("函数outer执行完毕,全局变量a的内存地址: ",id(a))
>>>
函数outer调用之前全局变量a的内存地址: 2060964686128
函数outer调用之时闭包外部的变量a的内存地址: 2060964686160
函数inner调用之后闭包内部变量a的内存地址: 2060964686192
函数inner调用之后,闭包外部的变量a的内存地址: 2060964686192
函数outer执行完毕,全局变量a的内存地址: 2060964686128
总结起来即:global声明使用全局变量,nonlocal声明使用函数嵌套关系外层的变量,即闭包外部的变量。
global !global! 运行改所有
nonlocal!~nonlocal!运行改函数内哟