Scope (2)
白鹤亮翅
在嵌套函数闭包中,内层函数自然能够向上读取外层作用域声明的变量:
def run():
flag = False
def print_flag():
print(flag)
print_flag() # False
run()
当场打脸
既然读取没问题,那我们在内层函数里顺手更新一下这个变量:
def run():
flag = False
def print_flag():
print(flag)
def set_to_true():
flag = True
print(flag)
set_to_true()
print(flag)
return flag
run()
运行输出却让人摸不着头脑:
$ python3 test.py
False
False
明明调用了 set_to_true(),外层的 flag 却纹丝未动。
见招拆招
在读取变量时,Python 按照 LEGB 规则(Local -> Enclosing -> Global -> Built-in)逐层向上查找,所以 print_flag() 能够顺利找到 run() 作用域里的 flag。
但赋值操作的逻辑完全不同:如果在当前作用域中给一个变量赋值,Python 会默认在当前局部作用域创建一个新的局部变量。所以在 set_to_true() 中,flag = True 只是创建了一个局部的 flag 并遮蔽(shadow)了外层变量,函数返回后即被销毁,丝毫没有影响外层的 flag。
在 Python 3 中,可以使用 nonlocal 关键字明确声明该变量来自外层闭包:
def set_to_true():
nonlocal flag
flag = True
再次执行即可符合预期:
$ python3 test.py
False
True
避坑防身:在工程实践中频繁使用 nonlocal 或 global 容易导致闭包状态隐晦难以维护,通常建议通过显式返回值、类属性或字典容器来传递和更新状态。
古法手工编程:从入门到放弃
