Python作用域规则疑问:两段嵌套函数代码为何运行结果不同?
这个问题的核心在于Python处理变量作用域的一个关键规则——只要函数内部存在对变量的赋值操作,Python就会把这个变量标记为该函数的局部变量,不管赋值语句写在代码的哪个位置。咱们分别拆解两段代码来理解:
第一段代码:正常运行的原因
先看这段能正常输出的代码:
name = "Sammy" def greet(): name = 'johny' def hello(): print('hello ' + name) # gets 'name' from the enclosing 'greet' hello() greet()
运行后输出hello johny,完全符合预期。
这里的关键是:hello()函数里只引用了name,没有对它做任何赋值操作。Python会按照LEGB规则(局部作用域Local → 外层嵌套作用域Enclosing → 全局作用域Global → 内置作用域Built-in)逐层查找变量:
- 首先在
hello自己的局部作用域找name,找不到; - 接着去外层的
greet函数作用域找,发现这里定义了name = 'johny',就直接用这个值; - 根本不会去全局作用域的
name = "Sammy"那里找,所以输出结果没问题。
第二段代码:报错的根源
再看这段触发错误的代码:
x = 50 def func1(): x = 20 def func2(): print("x is ", x) # Generates error here x = 2 print("Changed the local x to ",x) func2() func1() print("x is still ",x)
运行时抛出UnboundLocalError: local variable 'x' referenced before assignment。
问题出在func2()内部的x = 2这行代码:Python在编译func2的时候,只要看到函数内部有对x的赋值,就会立刻把x标记为func2的局部变量。
所以当func2执行到print("x is ", x)时,它会直接去func2自己的局部作用域找x,但此时x还没被赋值(赋值语句在后面),自然就会抛出“局部变量在赋值前被引用”的错误。哪怕外层的func1里有x=20、全局有x=50,Python也不会再去这些外层作用域查找x了——因为它已经认定x是func2的局部变量。
如何修复第二段代码?
如果想让func2引用并修改外层func1里的x,可以用nonlocal关键字明确声明x来自外层嵌套作用域:
x = 50 def func1(): x = 20 def func2(): nonlocal x # 告诉Python:x不是我自己的局部变量,去外层嵌套作用域找 print("x is ", x) x = 2 print("Changed the local x to ",x) func2() func1() print("x is still ",x)
修改后运行会输出:
x is 20 Changed the local x to 2 x is still 50
如果你的需求是引用全局的x,那就换成global关键字,但显然这里的场景更适合用nonlocal。
内容的提问来源于stack exchange,提问作者Aswin P J

