Python 变量作用域详解(Python 3.x)
变量作用域是指变量可被访问的代码范围。与 Java 等语言不同,Python 的变量作用域规则更灵活,尤其在全局变量和局部变量的区分上有特殊机制。本文将详细解析 Python 中变量的作用域类型、访问规则及修改全局变量的方法。
作用域的基本类型
Python 中变量的作用域分为以下四种,按访问优先级从高到低排列:
- 局部作用域(Local):在函数内部定义的变量,仅在函数内有效。
- 嵌套作用域(Enclosing):在嵌套函数外层的函数中定义的变量(适用于闭包场景)。
- 全局作用域(Global):在模块(.py 文件)顶层定义的变量,整个模块内可访问。
- 内置作用域(Built-in):Python 内置的变量或函数(如
print、len)。
查找顺序:L → E → G → B(由内向外)
局部变量(Local)
在函数内部定义的变量默认为局部变量,仅在函数执行期间存在,函数外部无法访问。
示例:局部变量的特性
# 全局变量 xx = 50deffunction_local(x_param):# 函数内部的 x 是局部变量(参数也是局部变量) print(f"函数内初始 x(参数):{x_param}") x_local = 2# 局部变量 print(f"函数内修改后局部 x:{x_local}")# 调用函数function_local(x)# 函数外部无法访问局部变量print(f"函数外全局 x:{x}") # 输出:50(不受函数内局部变量影响)# print(x_local) # 报错:NameError: name 'x_local' is not defined
输出结果:
函数内初始 x(参数):50函数内修改后局部 x:2函数外全局 x:50
结论:
- 函数内部定义的变量(如
x_local)仅在函数内有效。 - 局部变量与全局变量同名时,函数内优先使用局部变量。
全局变量(Global)
在模块顶层定义的变量为全局变量,可在模块内的任何函数外访问。若要在函数内修改全局变量,需用 global 关键字声明。
访问全局变量(无需声明)
函数内可直接访问全局变量(无需 global 声明):
# 全局变量greeting = "Hello"defprint_greeting():# 直接访问全局变量 print(greeting)print_greeting() # 输出:Hello
修改全局变量(需用 global 声明)
若在函数内直接修改全局变量,Python 会将其视为局部变量(创建新的局部变量),而非修改全局变量。如需修改,必须用 global 声明:
# 全局变量count = 0defincrement():# 声明使用全局变量 count(而非创建局部变量)global count count += 1# 修改全局变量 print(f"函数内 count:{count}")increment() # 输出:函数内 count:1print(f"函数外 count:{count}") # 输出:函数外 count:1(全局变量已被修改)
**如果不声明 global**:
count = 0defincrement_error(): count += 1# 报错:UnboundLocalError(Python 认为 count 是局部变量,但未初始化)increment_error()
global 关键字的作用
- 告诉 Python:“该变量是全局变量,不要创建局部变量”。
name = "Alice"defchange_name():global name # 声明在使用前 name = "Bob"# 修改全局变量change_name()print(name) # 输出:Bob
嵌套作用域(Enclosing)
当函数嵌套时,内层函数可访问外层函数的变量(非全局),这种作用域称为嵌套作用域。若要修改外层函数的变量,需用 nonlocal 关键字(Python 3 新增)。
示例:嵌套函数与 nonlocal
defouter():# 外层函数的变量(嵌套作用域) message = "Hello from outer"definner():# 声明使用外层函数的 message(非局部,非全局)nonlocal message message = "Hello from inner"# 修改外层变量 print(f"内层函数:{message}") inner() print(f"外层函数:{message}") # 输出修改后的值outer()
输出结果:
内层函数:Hello from inner外层函数:Hello from inner
说明:
- 若不用
nonlocal,内层函数的 message 会被视为局部变量。 nonlocal 仅用于嵌套作用域,不可用于全局变量。
闭包(Closure)
当内层函数返回并被外部使用时,外层函数的变量会被“记住”,这就是闭包。
defmake_multiplier(n):"""创建一个乘以 n 的函数"""defmultiplier(x):return x * n # n 来自外层函数return multiplier# 创建两个不同的闭包double = make_multiplier(2)triple = make_multiplier(3)print(double(5)) # 10print(triple(5)) # 15print(triple(10)) # 30# 查看闭包捕获的变量print(double.__closure__[0].cell_contents) # 2
内置作用域(Built-in)
Python 内置的名称
Python 预定义了一些内置函数和异常,它们位于 builtins 模块中。
# 常用内置函数print(len([1, 2, 3])) # 3print(max(10, 20, 30)) # 30print(type(42)) # <class 'int'># 查看所有内置名称# import builtins# print(dir(builtins))
内置作用域被遮蔽的问题
如果不小心定义了与内置函数同名的变量,会“遮蔽”内置函数。
# ⚠️ 严重问题:遮蔽了内置的 len 函数len = 100# 创建了全局变量 lendefget_length(items):return len(items) # ❌ TypeError: 'int' object is not callable# 恢复:删除自己定义的 lendel lenprint(len([1, 2, 3])) # 3(恢复)
最佳实践:避免使用内置名称作为变量名(如 len、list、dict、str、type 等)
作用域查找规则:LEGB 原则
当访问一个变量时,Python 按以下顺序查找:
- E(Enclosing):外层函数的变量(嵌套作用域)。
- B(Built-in):Python 内置的变量或函数。
若找不到变量,会抛出 NameError。
示例:LEGB 原则演示
# 全局变量(G)x = "global"defouter():# 嵌套作用域变量(E) x = "outer"definner():# 局部变量(L) x = "inner" print(x) # 优先使用局部变量(L)→ 输出:inner inner() print(x) # 外层变量(E)→ 输出:outerouter()print(x) # 全局变量(G)→ 输出:global# 访问内置函数(B)print(len([1, 2, 3])) # 调用内置的 len 函数 → 输出:3
常见问题与最佳实践
**区分 global 和 nonlocal**:
nonlocal:用于修改嵌套作用域的变量(外层函数)。
局部变量遮蔽(Shadowing):
x = 100defshadow(): x = 200# 局部变量,遮蔽全局 x print(x) # 输出:200shadow()print(x) # 输出:100(全局 x 未变)
- 若局部变量与全局变量同名,局部变量会 “遮蔽” 全局变量(函数内优先使用局部)。