鱼C论坛

 找回密码
 立即注册
查看: 131|回复: 2

[技术交流] 高手也容易忽略的 Python 命名空间与作用域

[复制链接]
发表于 2025-4-8 10:32:57 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
想学好 Python ,一定要掌握变量作用域,因为它会像幽灵👻一样,时不时跳出来吓你一跳。你是不是遇到过这种情况:
  1. x = 10

  2. def func():
  3.     print(x)

  4. func()  # 还能跑


  5. def func2():
  6.     print(y)
  7.     y = 20

  8. func2()  # 报错!UnboundLocalError: local variable 'y' referenced before assignment
复制代码

同样是 print(x),x 正常输出,而 y 却报错了?Python 你是不是针对我?!
别慌,今天我们就来彻底搞清楚 Python 的命名空间(Namespace)和作用域(Scope),保证你下次见到类似问题不再抓狂。
1. 什么是命名空间?
命名空间(Namespace) 就是一个存储变量名和对应值的地方,可以理解成一个大字典,里面存着所有变量的名称和它们的值。Python 里的命名空间主要有三类:
内置命名空间(Built-in Namespace):Python 自带的,比如 print()、len() 这些常用函数。
全局命名空间(Global Namespace):在模块层级(也就是 .py 文件的最外层)定义的变量、函数等。
局部命名空间(Local Namespace):函数内部定义的变量、参数等。
简单理解:
内置命名空间 是 Python 自带的词典。
全局命名空间 是你写的 .py 文件中的变量字典。
局部命名空间 是函数内部专属的小字典。
来看个示例:
  1. # 全局命名空间
  2. global_var = "我是全局变量"

  3. def my_func():
  4.     # 局部命名空间
  5.     local_var = "我是局部变量"
  6.     print(local_var)

  7. print(global_var)  # ✅ 正常打印
  8. my_func()  # ✅ 正常打印
  9. print(local_var)  # ❌ NameError: name 'local_var' is not defined
复制代码

函数 my_func() 里面的 local_var 只能在函数内访问,出了函数就不认识了。
2. 什么是作用域?
作用域(Scope)决定了变量能在代码的哪些位置被访问。Python 遵循 LEGB 规则,即:
Local(局部作用域):当前函数内部的命名空间。
Enclosing(闭包作用域):嵌套函数(函数里面再定义函数)外层函数的命名空间。
Global(全局作用域):当前模块的命名空间。
Built-in(内置作用域):Python 预定义的命名空间。
如果 Python 需要访问一个变量,它会按照 LEGB 顺序查找。来看个案例:
  1. x = "全局变量"

  2. def outer():
  3.     x = "外部函数变量"
  4.    
  5.     def inner():
  6.         x = "内部函数变量"
  7.         print(x)  # 优先找最近的 x
  8.    
  9.     inner()
  10.     print(x)  # 找不到 inner 里的 x,找外部函数的

  11. outer()
  12. print(x)  # 直接找全局的
复制代码

先别急着看结果,仔细思考下会输出什么?相信好多人会栽在这里
输出结果:
  1. 内部函数变量
  2. 外部函数变量
  3. 全局变量
复制代码

这个例子很清楚地展示了 LEGB 规则。Python 先找局部变量(Local),找不到就往外层找,一直找到内置命名空间为止。
3. global 和 nonlocal 关键字
有时候我们希望在函数内部修改外部变量,比如:
  1. x = 10

  2. def change_global():
  3.     global x  # 说明我们要修改全局变量 x
  4.     x = 20

  5. change_global()
  6. print(x)  # 20
复制代码

如果你不加 global,x = 20 只会创建一个新的局部变量,不会影响全局 x。
类似的,nonlocal 用来修改闭包作用域的变量:
  1. def outer():
  2.     x = "外部变量"
  3.    
  4.     def inner():
  5.         nonlocal x  # 说明我们要修改外部函数的 x
  6.         x = "修改后的变量"
  7.    
  8.     inner()
  9.     print(x)  # "修改后的变量"

  10. outer()
复制代码

如果不加 nonlocal,inner() 里改的 x 只是一个新的局部变量,不会影响 outer() 里的 x。
4. 常见作用域坑
4.1 for 循环里的变量是全局的!
  1. for i in range(5):
  2.     pass

  3. print(i)  # 4 (在别的语言里可能报错)
复制代码

Python 里 for 循环变量 i 是全局的,循环结束后仍然存在!
4.2 默认参数的坑
  1. def func(x=[]):
  2.     x.append(1)
  3.     print(x)

  4. func()  # [1]
  5. func()  # [1, 1](惊不惊喜?)
复制代码

原因是默认参数 x=[] 只初始化一次,后续调用会复用这个列表!
5. 总结
Python 的命名空间可以理解成存变量的字典,有 内置、全局、局部 三种。
Python 遵循 LEGB 规则 查找变量,从局部到全局依次查找。
global 关键字用来修改全局变量,nonlocal 关键字用来修改闭包变量。
for 循环变量是全局的,不像其他语言会自动销毁。
默认参数的可变对象会复用,导致意外行为。
希望这篇文章能让你彻底搞清楚 Python 的作用域和命名空间!下次遇到变量找不到的问题,就从 LEGB 规则入手排查吧!

转自:花小姐的春天
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2025-4-8 10:36:09 | 显示全部楼层
高手也容易忽略的 Python 命名空间与作用域解析

为什么 func2() 会报错?
问题核心:UnboundLocalError 是由于 Python 的 变量声明提前判定机制

  1. def func2():
  2.     print(y)  # 编译器发现这里有赋值语句 y=20
  3.     y = 20    # 所以判定 y 是局部变量!
复制代码

Python 在编译阶段发现函数内有 y=20,于是将 y 标记为局部变量。执行时 print(y) 会先找局部变量,但此时 y 尚未赋值,故报错!

核心知识点梳理

  • LEGB 查找顺序
    查找变量时按照:Local → Enclosing → Global → Built-in

    1. x = "全局"
    2. def outer():
    3.     x = "闭包"
    4.     def inner():
    5.         x = "局部"
    6.         print(x)  # 输出"局部"
    7.     inner()
    8. outer()
    复制代码

  • global 与 nonlocal 对比

    1. # global 修改全局变量
    2. x = 1
    3. def func():
    4.     global x
    5.     x = 2

    6. # nonlocal 修改闭包变量
    7. def outer():
    8.     y = 1
    9.     def inner():
    10.         nonlocal y
    11.         y = 2
    复制代码

  • 经典踩坑案例

    1. # 案例1:for循环变量泄露
    2. for i in range(3):
    3.     pass
    4. print(i)  # 输出2(其他语言会报错)

    5. # 案例2:默认参数陷阱
    6. def bug(a=[]):
    7.     a.append(1)
    8.     return a
    9. bug()  # [1]
    10. bug()  # [1,1] (默认参数是同一个列表!)
    复制代码



避坑指南
  • 修改全局变量必须显式使用 global
  • 嵌套函数修改外层变量必须用 nonlocal
  • 默认参数避免使用可变对象(如列表、字典)
  • del 主动清理不再使用的变量


转自:花小姐的春天

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2025-4-8 13:51:35 | 显示全部楼层
本帖最后由 jackz007 于 2025-4-8 15:30 编辑
  1. x = 10               # 定义全局变量 x = 10

  2. def func() :
  3.     print(x)         # 全局变量 x 在所有的函数内一律可读(只能被访问,不可被赋值),在函数内经 global x 声明过后,在该函数内可写(允许被赋值)。

  4. def func2() :
  5.     print(y)         # 变量 y 无根,属于局部变量,局部变量必须先定义(赋值),后访问,由于此前变量 y 不曾被赋值,所以,此句会报错
  6.     y = 20           # 定义局部变量 y 为整型数 20

  7. func()
  8. func2()
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-5-5 19:29

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表