人人都会AI编程

6.4 命名空间:局部、全局、内置命名空间与查找顺序

更新时间:2026-07-12

在 Python 里,一个变量名到底指向哪个对象,并不是靠声明决定的,而是由命名空间查找顺序共同决定。理解这两点,你就掌握了变量作用域的核心原理,也能一眼看出“变量找不到”或“被意外覆盖”这类问题的根源。

什么是命名空间

  • 命名空间就是名字到对象的映射,你可以把它想象成一个字典:{‘x’: 10, ‘func’: <function>}
  • Python 中有多种命名空间,它们独立存在,互不干扰。同一个名字在不同命名空间里可以指向完全不同的对象。
  • 每个模块、每个函数调用、每个类定义都会创建自己的命名空间。

三层命名空间

按照生命周期的长短和可见范围,命名空间可以分为三类:

1. 局部命名空间 (Local)

  • 在哪里:函数或方法内部。
  • 何时创建/销毁:函数被调用时创建,函数返回或抛出异常时销毁。
  • 包含什么:函数内部定义的变量、参数。
  • 特点:只在当前函数内可见,函数外无法访问。
def foo():
    x = 10        # x 在 foo 的局部命名空间里
    print(x)

foo()
# print(x)      # 报错 NameError,外部看不到局部变量

2. 全局命名空间 (Global)

  • 在哪里:模块级别(.py 文件顶层)。
  • 何时创建/销毁:模块被导入时创建,解释器退出时销毁。
  • 包含什么:模块顶层定义的变量、函数、类,以及通过 import 引入的名字。
  • 特点:该模块内的所有函数、类都可以读取全局变量,但要修改它需要用 global 声明。
x = 10          # 全局变量

def bar():
    print(x)    # OK,读取全局变量

def baz():
    global x    # 声明要修改全局的 x
    x = 20

3. 内置命名空间 (Built-in)

  • 在哪里builtins 模块里。
  • 何时创建/销毁:Python 解释器启动时创建,一直存在到退出。
  • 包含什么:内置函数(printlenrange)、内置异常(Exception)、内置常量(TrueNone)。
  • 特点:任何模块、任何函数都可以直接使用,不需要导入。
print(len('hello'))   # print 和 len 都来自内置命名空间

另外,闭包还有一层“外层函数”的命名空间(Enclosing),我们放到作用域查找顺序里一起解释。

查找顺序:LEGB 规则

当你在代码里写一个变量名 name 时,Python 按 L → E → G → B 的顺序依次查找:

  1. L (Local):当前函数的局部命名空间
  2. E (Enclosing):外层函数的局部命名空间(如果有嵌套函数)
  3. G (Global):当前模块的全局命名空间
  4. B (Built-in):内置命名空间

一旦在某个层次找到了这个名字,就停止搜索,直接使用该名字对应的对象。如果四个层次都找不到,就抛出 NameError

实操示例

x = 'global'          # G 命名空间

def outer():
    x = 'enclosing'   # E 命名空间(对于 inner 来说)
    
    def inner():
        x = 'local'   # L 命名空间(inner 自己的局部)
        print(x)      # 打印 'local',先在 L 找到
    
    inner()

outer()

如果把 inner() 里的赋值注释掉:

def outer():
    x = 'enclosing'
    def inner():
        print(x)      # 打印 'enclosing',L 没有,找 E
    inner()

如果 outer() 里的赋值也去掉,就会打印全局的 'global',最后才轮到内置命名空间(比如 print 函数自身的名字)。

赋值的默认行为

  • 在函数内部直接写 x = ...,一定是在当前函数的局部命名空间创建或修改变量,不会自动去改全局变量。这正是许多新手犯错的根源:以为改的是全局,实际是新建局部。
  • 要修改全局变量,必须在函数内用 global x 声明;要修改外层函数变量(闭包场景),用 nonlocal x 声明。

两个实用的查看工具

  • locals():返回当前局部命名空间的字典(在模块级别调用和 globals() 相同)。
  • globals():返回当前全局命名空间的字典。

在调试时,你可以在函数内打印 locals() 看看当前有哪些变量及其值,非常直观。

命名空间是 Python 变量作用域机制的底层基础,把握住 LEGB 查找规则和赋值默认行为,就能快速定位和避免作用域相关的坑,写出的代码逻辑也更清晰可控。