函数作用域

函数作用域

  • L(Local):最内层,包含局部变量,比如一个函数/方法内部。
  • E(Enclosing):包含了非局部(non-local)也非全局(non-global)的变量。比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 nonlocal。
  • G(Global):当前脚本的最外层,比如当前模块的全局变量。
  • B(Built-in): 包含了内建的变量/关键字等。最后被搜索

规则顺序: L –> E –> G –>G-> B,从内向外一层一层搜索

当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了。

  1. 比如说修改全局变量值
num = 1
def fun1():
global num # 需要使用 global 关键字声明
print(num)
num = 123
print(num)
fun1()
print(num)
  1. 如果要修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量则需要 nonlocal 关键字了,如下
def outer():
num = 10
def inner():
nonlocal num # nonlocal关键字声明
num = 100
print(num)
inner()
print(num)
outer()
#如下形式是会报错的,因为 test 函数中的 a 使用的是局部,未定义,无法修改。
a = 10
def test():
a = a + 1
print(a)
test()
#修改 a 为全局变量,通过函数参数传递,可以正常执行输出结果为:
a = 10
def test(a):
a = a + 1
print(a)
test(a)
#这种方法等价于
a = 10
def test():
global a
a = a + 1
print(a)
test()

说明=其实在函数中起到名称和值绑定的作用,如果我们对外层list进行append或者pop操作则不会报错,比如

a = []
def test():
a.append(1)
print(a)
test()

总结:在相对内部空间使用某个变量的时候,如果仅是调用名称获取值,那么会从内向外一层一层去找,是可以的。但是如果对其重新a = a+1形式的话是不行的;如果a = 3就相当于在内部又定义了个a,不会改变外部a的值;但a.append, a.pop这类是可以的。

如果想从内部修改外部的值,最好的方法就是声明global或者nonlocal。

平时应该很难遇上,写递归的时候如果有参数更新,需要注意。