Python3 命名空间和作用域详解

在 Python 中,命名空间(Namespace)和作用域(Scope)是理解变量访问规则的核心概念。它们共同决定了 “变量在哪里定义” 以及 “在哪里可以被访问”,直接影响代码的可读性和正确性。本文将从基础定义出发,结合实例详解 Python3 的命名空间与作用域规则。

一、命名空间:变量的 “容器”

命名空间本质是一个映射表(键值对集合),其中 “键” 是变量名,“值” 是变量指向的对象(如数值、函数、类等)。它的核心作用是避免变量名冲突—— 不同命名空间中可以有同名变量,彼此互不干扰。

1. 命名空间的类型

Python 中命名空间按 “定义位置” 和 “生命周期” 可分为 4 类,从 “最内层” 到 “最外层” 依次为:

命名空间类型定义范围包含内容生命周期
局部命名空间 函数 / 方法内部 函数参数、函数内定义的变量 / 函数 / 类 函数调用时创建,执行结束后销毁
非局部命名空间 嵌套函数的外层函数内部 外层函数定义的变量 / 函数 / 类 外层函数调用时创建,执行结束后销毁
全局命名空间 模块(.py 文件)级 模块内定义的变量 / 函数 / 类、导入的模块 模块加载时创建,解释器退出或模块卸载时销毁
内置命名空间 Python 解释器内置 内置函数(如printlen)、内置类型等 Python 解释器启动时创建,退出时销毁

2. 命名空间的访问方式

Python 提供了两个内置函数用于查看当前命名空间的内容:

  • locals():返回当前局部命名空间的字典(可修改,修改会影响局部变量)。
  • globals():返回当前全局命名空间的字典(修改会影响全局变量)。

示例
 
 
# 全局命名空间:模块级变量
global_var = "我是全局变量"

def func():
    # 局部命名空间:函数内变量
    local_var = "我是局部变量"
    print("局部命名空间:", locals())  # 包含local_var

func()
print("全局命名空间:", globals()["global_var"])  # 访问全局变量
 

输出:
局部命名空间: {'local_var': '我是局部变量'}
全局命名空间: 我是全局变量
 

二、作用域:命名空间的 “访问范围”

作用域是指命名空间的可访问范围—— 即 “在代码的哪个部分可以引用该命名空间中的变量”。它不是物理容器,而是一套 “访问规则”,决定了变量查找的顺序。

1. 作用域的类型(LEGB 规则)

Python 中变量的查找遵循LEGB 规则(从内到外依次查找,找到即停止):

  • L(Local):当前函数 / 方法的局部作用域(对应局部命名空间)。
  • E(Enclosing):嵌套函数的外层函数作用域(对应非局部命名空间)。
  • G(Global):当前模块的全局作用域(对应全局命名空间)。
  • B(Built-in):Python 内置作用域(对应内置命名空间)。

若在所有作用域中都未找到变量,则抛出NameError

2. 作用域示例:LEGB 规则的实际应用

 
# 内置命名空间:内置函数len
# 全局命名空间:模块级变量a
a = 10

def outer():
    # 非局部命名空间:外层函数变量b
    b = 20
    def inner():
        # 局部命名空间:内层函数变量c
        c = 30
        # 查找变量x:依次检查L→E→G→B
        print(x)  # ???x在哪里定义?
    inner()

outer()

  • xinner()内定义(c=30改为x=30),则 L 作用域找到,输出30
  • x未在inner()内定义,但在outer()内定义(b=20改为x=20),则 E 作用域找到,输出20
  • x未在outer()内定义,但在模块级定义(a=10改为x=10),则 G 作用域找到,输出10
  • x未在模块级定义,但x是内置函数(如x=len),则 B 作用域找到,输出<built-in function len>
  • 若以上都没有,则抛出NameError: name 'x' is not defined

三、变量作用域的特殊场景

1. 局部变量与全局变量的 “遮蔽”

当局部变量与全局变量同名时,局部变量会 “遮蔽” 全局变量(即优先访问局部变量):
 
 
num = 100  # 全局变量

def func():
    num = 200  # 局部变量(遮蔽全局变量)
    print("局部num:", num)  # 输出200

func()
print("全局num:", num)  # 输出100(全局变量未被修改)
 

2. 修改外层作用域的变量:globalnonlocal关键字

默认情况下,在局部作用域中不能直接修改外层作用域的变量(只能读取)。若需修改,需用关键字声明:

  • global:声明变量为 “全局变量”(修改全局命名空间中的变量)。
  • nonlocal:声明变量为 “非局部变量”(修改嵌套外层函数的变量,不适用于全局变量)。

global示例
 
 
global_var = 10  # 全局变量

def modify_global():
    global global_var  # 声明:要修改全局变量
    global_var = 20   # 成功修改

modify_global()
print(global_var)  # 输出20(全局变量被修改)
 

nonlocal示例(嵌套函数):
 
 
def outer():
    outer_var = 100  # 外层函数变量(非局部命名空间)
    
    def inner():
        nonlocal outer_var  # 声明:要修改外层函数变量
        outer_var = 200    # 成功修改
    
    inner()
    print(outer_var)  # 输出200(外层变量被修改)

outer()
 

注意

  • nonlocal不能用于修改全局变量(会报错)。
  • 若未声明global/nonlocal,在局部作用域中对变量赋值时,Python 会默认将其视为 “新的局部变量”(而非修改外层变量)。

3. 模块与类的命名空间 / 作用域

  • 模块:每个.py文件都是一个模块,拥有独立的全局命名空间。不同模块的全局变量互不干扰(如module1.varmodule2.var可同名)。
  • :类定义会创建独立的命名空间(类属性、方法存储其中);类的实例也有自己的命名空间(实例属性)。访问规则为:先查实例命名空间,再查类命名空间。
 
 
class MyClass:
    class_var = "类属性(类命名空间)"  # 类命名空间
    
    def __init__(self):
        self.instance_var = "实例属性(实例命名空间)"  # 实例命名空间

obj = MyClass()
print(obj.instance_var)  # 实例命名空间:输出“实例属性...”
print(obj.class_var)     # 类命名空间:输出“类属性...”
 

四、常见误区与总结

  1. 误区:“局部作用域可以修改外层变量”—— 实际默认只能读取,修改需用global/nonlocal
  2. 误区:“locals()globals()返回的是副本”—— 实际返回的是命名空间的引用,修改字典会直接影响变量。
  3. 核心规则:变量查找遵循 LEGB 顺序,作用域决定 “能否访问”,命名空间决定 “变量存储位置”。

通过理解命名空间的 “容器” 角色和作用域的 “访问规则”,可以更清晰地掌控 Python 变量的行为,避免因变量名冲突或访问权限导致的错误。记住 LEGB 规则和global/nonlocal的用法,是掌握 Python 变量作用域的关键。

posted on 2025-08-05 09:50  小陶coding  阅读(78)  评论(0)    收藏  举报