Python中的变量作用域与LEGB规则
字数 1034 2025-11-11 03:21:18
Python中的变量作用域与LEGB规则
1. 问题描述
在Python中,当访问一个变量时,解释器会按照特定顺序在多个作用域中查找该变量。这一顺序被总结为LEGB规则,即Local(局部)→ Enclosing(嵌套)→ Global(全局)→ Built-in(内置)。理解LEGB规则对于避免变量名冲突、正确使用闭包和装饰器至关重要。
2. 作用域层级详解
2.1 Local(局部作用域)
- 范围:当前函数或方法内部。
- 特点:在函数内定义的变量(如
x = 1)仅在该函数内可见。
def func():
x = 10 # 局部变量
print(x) # 输出 10
func()
# print(x) # 报错:x在外部未定义
2.2 Enclosing(嵌套作用域)
- 范围:外层嵌套函数的作用域(例如闭包中的外部函数)。
- 特点:当内层函数访问外层函数的变量时,会在此作用域查找。
def outer():
y = 20 # 嵌套作用域变量
def inner():
print(y) # 访问外层函数的y
inner()
outer() # 输出 20
2.3 Global(全局作用域)
- 范围:当前模块(文件)顶层定义的变量。
- 特点:在函数外定义的变量,或在函数内用
global声明的变量。
z = 30 # 全局变量
def func():
print(z) # 访问全局变量
func() # 输出 30
2.4 Built-in(内置作用域)
- 范围:Python内置的全局变量(如
print、len等)。 - 特点:当变量在其他作用域未找到时,最后查找内置作用域。
def func():
print(len("abc")) # len来自内置作用域
func() # 输出 3
3. LEGB查找顺序的验证
通过以下代码演示查找顺序:
x = "global" # 全局变量
def outer():
x = "enclosing" # 嵌套作用域变量
def inner():
x = "local" # 局部变量
print(x) # 优先查找局部作用域
inner()
outer() # 输出 "local"
逐步分析:
inner()内部访问x时,先在局部作用域找到x="local",直接使用。- 若注释掉
x="local",则会查找嵌套作用域(x="enclosing")。 - 若嵌套作用域也没有
x,则查找全局作用域(x="global")。 - 最后尝试内置作用域(如未定义
x则报错NameError)。
4. 关键字global与nonlocal的作用
4.1 global
用于在函数内部修改全局变量:
x = 10
def func():
global x # 声明x为全局变量
x = 20 # 修改全局变量x
func()
print(x) # 输出 20
4.2 nonlocal
用于在嵌套函数中修改外层函数的变量(非全局):
def outer():
x = 10
def inner():
nonlocal x # 声明x为嵌套作用域变量
x = 20
inner()
print(x) # 输出 20
outer()
5. 常见错误与注意事项
- 局部变量未定义错误:
def func():
print(y) # 错误:y未在局部定义,且外部无y
y = 1
原因:赋值语句y=1会让y被解释为局部变量,但打印时尚未赋值。
- 闭包变量捕获问题:
functions = []
for i in range(3):
def func():
return i
functions.append(func)
print([f() for f in functions]) # 输出 [2, 2, 2]
原因:闭包中的i是嵌套作用域变量,最终值为循环结束值2。
解法:使用默认参数或lambda绑定当前值:
# 方法1:默认参数
def func(i=i):
return i
# 方法2:lambda
functions = [lambda i=i: i for i in range(3)]
6. 总结
- LEGB规则是Python变量查找的基石,影响函数设计、闭包和装饰器。
- 使用
global和nonlocal可跨作用域修改变量,但需谨慎避免污染全局状态。 - 在循环中创建闭包时,注意变量绑定的时机,必要时通过参数固化值。