在 Python 中,变量的作用域决定了它在代码中哪些位置可以被访问。根据 LEGB 规则,Python 查找变量时遵循以下四种作用域的先后顺序:
- L(Local)局部作用域
- E(Enclosing)外层函数作用域(也称闭包作用域)
- G(Global)全局作用域
- B(Built-in)内建作用域
下面逐一说明。
1. 局部作用域(Local)
-
定义:函数内部定义的变量拥有局部作用域,只在函数体内可见。
-
生命周期:函数调用时创建,函数返回或抛出异常时销毁。
-
访问限制:函数外部无法直接访问局部变量。
-
示例 :
pythondef func(): x = 10 # 局部变量 print(x) # 可以访问 func() # print(x) # 错误:NameError,外部无法访问
2. 外层函数作用域(Enclosing)
-
定义:出现在嵌套函数中,外层函数(包含内层函数的函数)内的变量,对于内层函数来说就是外层作用域变量。
-
特点 :内层函数可以读取外层函数的变量,但若要修改,需要使用
nonlocal关键字。 -
用途:实现闭包(closure)或装饰器。
-
示例:
pythondef outer(): y = 20 # 外层作用域变量 def inner(): print(y) # 内层函数可以访问 y inner() outer()修改外层变量:
pythondef outer(): y = 20 def inner(): nonlocal y # 声明要修改外层变量 y = 30 inner() print(y) # 输出 30
3. 全局作用域(Global)
-
定义:在模块顶层(非函数、非类内部)定义的变量,在整个模块内均可访问。
-
访问限制 :函数内可以读取全局变量,但若要修改,必须使用
global关键字。 -
示例 :
pythonz = 100 # 全局变量 def foo(): print(z) # 可以读取 foo() def bar(): global z # 声明要修改全局变量 z = 200 bar() print(z) # 输出 200
4. 内建作用域(Built-in)
-
定义 :Python 解释器启动时自动加载的内置命名空间,包含所有内置函数(如
print、len)、异常类(如ValueError)、常量(如True、False)等。 -
位置 :
builtins模块(Python 3 中)。 -
优先级:最低,只有在局部、外层、全局都找不到变量时,才会去内建作用域查找。
-
示例 :
python# 即使没有定义 len,Python 也会从 builtins 中找到 len 函数 print(len("hello")) # 输出 5 # 不建议,但可以故意覆盖内建名称 len = 10 # 在全局作用域中覆盖了内建 len # print(len("abc")) # 现在会出错,因为 len 变成了整数
LEGB 查找顺序
当在代码中使用一个变量时,Python 按照 L → E → G → B 的顺序依次查找:
- 先查当前函数的 局部作用域。
- 如果没找到,向上查任何外层函数(嵌套情况)的 外层作用域。
- 再没找到,查模块级别的 全局作用域。
- 最后查 内建作用域。
- 如果依然找不到,抛出
NameError。
python
x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print(x) # 输出 "local"
inner()
outer()
总结表
| 作用域 | 位置 | 如何修改外部变量 | 生命周期 |
|---|---|---|---|
| 局部 (L) | 函数内部 | 无需声明,直接赋值即创建局部变量 | 函数调用期间 |
| 外层 (E) | 嵌套函数的外层函数 | 使用 nonlocal 声明 |
外层函数调用期间 |
| 全局 (G) | 模块顶层(任何函数/类外部) | 使用 global 声明 |
模块被加载到解释器结束 |
| 内建 (B) | builtins 模块 |
不可直接修改(覆盖会导致错误) | Python 解释器运行期间始终存在 |
理解 LEGB 规则有助于写出清晰、无歧义的代码,并避免意外的变量遮蔽(shadowing)。