目录
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!运行改函数内哟