2025-08-21 Python进阶8——命名空间作用域
文章目录
- 1 命名空间(Namespace)
- 1.1 命名空间的定义
- 1.2 命名空间的类型
- 1.3 命名空间的查找顺序
- 1.4 命名空间的生命周期
- 1.5 命名空间实例
- 2 作用域(Scope)
- 2.1 作用域的定义
- 2.2 作用域的类型(LEGB 规则)
- 2.3 作用域实例
- 2.4 作用域的特殊说明
- 3 全局变量和局部变量
- 3.1 全局变量(Global Variables)
- 3.2 局部变量(Local Variables)
- 3.3 全局变量与局部变量的冲突
- 4 global 和 nonlocal
- 4.1 global 关键字
- 4.2 nonlocal 关键字
- 4.3 常见错误与解决
- 5 总结
1 命名空间(Namespace)
1.1 命名空间的定义
命名空间是从名称到对象的映射,大多数命名空间通过 Python 字典实现。它的主要作用是避免名称冲突,不同命名空间中的同名名称不会相互干扰。
可以将命名空间类比为文件系统:
- 每个文件夹(命名空间)中不能有相同的文件名(名称)
- 不同文件夹(命名空间)中可以有相同的文件名(名称)
1.2 命名空间的类型
Python 中主要有三种命名空间:
- 内置名称(built-in names)
- Python 语言内置的名称
- 例如:函数名
abs()
、print()
;异常名称BaseException
、Exception
等 - 这些名称在 Python 解释器启动时就存在
- 全局名称(global names)
- 模块中定义的名称
- 包括:函数、类、导入的模块、模块级变量和常量
- 在模块被加载时创建,通常持续到解释器退出
- 局部名称(local names)
- 函数或类中定义的名称
- 包括:函数参数、局部变量、内部函数等
- 在函数 / 类被调用时创建,在执行结束后销毁
1.3 命名空间的查找顺序
当使用一个名称时,Python 会按以下顺序查找:
- 局部命名空间(当前函数 / 类内部)
- 全局命名空间(当前模块)
- 内置命名空间(Python 内置)
如果在所有命名空间中都找不到该名称,会引发 NameError
异常:
print(undefined_variable) # 报错:NameError: name 'undefined_variable' is not defined
1.4 命名空间的生命周期
- 内置命名空间:在 Python 解释器启动时创建,直到解释器退出时销毁
- 全局命名空间:在模块被导入时创建,通常直到解释器退出时销毁
- 局部命名空间:在函数 / 类被调用时创建,在执行结束后销毁
特点:无法从外部命名空间访问内部命名空间的对象
1.5 命名空间实例
# 全局命名空间
var1 = 5def some_func():# 局部命名空间(some_func的)var2 = 6def some_inner_func():# 局部命名空间(some_inner_func的)var3 = 7
2 作用域(Scope)
2.1 作用域的定义
作用域是 Python 程序中可以直接访问某个命名空间的文本区域。“直接访问” 指无需使用前缀即可引用名称。
2.2 作用域的类型(LEGB 规则)
Python 中有四种作用域,遵循 LEGB 查找顺序:
- L(Local,局部作用域)
- 最内层作用域
- 包含当前函数 / 方法内部定义的变量
- 例如:函数的参数、在函数内定义的变量
- E(Enclosing,嵌套作用域)
- 包含非局部也非全局的变量
- 适用于嵌套函数场景,指外层函数的作用域
- 例如:函数 A 中定义了函数 B,则 A 的作用域对 B 来说是嵌套作用域
- G(Global,全局作用域)
- 当前模块的最外层作用域
- 包含模块级别的变量、函数、类等
- B(Built-in,内置作用域)
- 包含 Python 内置的名称
- 最后被搜索的作用域
查找顺序:Local → Enclosing → Global → Built-in
2.3 作用域实例
g_count = 0 # 全局作用域(G)def outer():o_count = 1 # 嵌套作用域(E,对于inner来说)def inner():i_count = 2 # 局部作用域(L)print(i_count) # 查找顺序:L → E → G → Binner()
outer()
2.4 作用域的特殊说明
-
只有模块、类、函数(
def
、lambda
)会创建新的作用域 -
以下代码块
不会
创建新的作用域:
if/elif/else
条件语句try/except
异常处理语句for/while
循环语句with
语句- 代码块(
{}
)
实例:
# if语句不会创建新作用域
if True:msg = 'Hello'
print(msg) # 可以访问,输出:Hello# 循环语句不会创建新作用域
for i in range(3):num = i
print(num) # 可以访问,输出:2
3 全局变量和局部变量
3.1 全局变量(Global Variables)
- 定义在函数外部的变量
- 可以在整个模块中被访问(函数内外均可)
- 如果函数内部没有同名局部变量,函数可以直接访问全局变量
x = 10 # 全局变量def my_function():print(x) # 可以访问全局变量my_function() # 输出:10
3.2 局部变量(Local Variables)
- 定义在函数内部的变量
- 仅在定义它的函数内部有效
- 函数外部无法访问
- 优先级高于全局变量(同名时优先使用局部变量)
def my_function():x = 5 # 局部变量print(x) # 访问局部变量my_function() # 输出:5
print(x) # 报错:NameError: name 'x' is not defined
3.3 全局变量与局部变量的冲突
当函数内部存在与全局变量同名的局部变量时,局部变量会覆盖全局变量:
total = 0 # 全局变量def sum(arg1, arg2):total = arg1 + arg2 # 局部变量,与全局变量同名print("函数内(局部):", total) # 输出:30return totalsum(10, 20)
print("函数外(全局):", total) # 输出:0(全局变量未被修改)
4 global 和 nonlocal
4.1 global 关键字
用于在函数内部修改全局变量,声明变量为全局变量:
num = 1 # 全局变量def fun1():global num # 声明num为全局变量print(num) # 输出:1num = 123 # 修改全局变量print(num) # 输出:123fun1()
print(num) # 输出:123(全局变量已被修改)
注意:如果在函数中仅读取全局变量而不修改,不需要使用 global
声明
4.2 nonlocal 关键字
用于在嵌套函数中修改外层函数的变量(非全局变量):
def outer():num = 10 # 外层函数的变量def inner():nonlocal num # 声明num为外层函数的变量num = 100 # 修改外层函数的变量print(num) # 输出:100inner()print(num) # 输出:100(外层变量已被修改)outer()
4.3 常见错误与解决
尝试在函数中修改全局变量而不使用 global
声明:
a = 10def test():a = a + 1 # 错误:试图修改全局变量但未声明print(a)test() # 报错:UnboundLocalError: local variable 'a' referenced before assignment
解决方法:
a = 10def test():global a # 声明为全局变量a = a + 1print(a) # 输出:11test()
5 总结
- 命名空间是名称到对象的映射,避免名称冲突,主要有三种类型:内置、全局、局部
- 作用域是可以直接访问命名空间的区域,遵循 LEGB 查找规则
- 全局变量在函数外定义,可在整个模块访问;局部变量在函数内定义,仅在函数内有效
global
关键字用于在函数内修改全局变量nonlocal
关键字用于在嵌套函数中修改外层函数的变量- 只有模块、类、函数会创建新的作用域,其他代码块不会