查看: 132|回复: 0

Python全局变量与局部变量作用域及NameError解析

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
在 Python 里讨论全局变量和局部变量,最容易踩坑的不是概念本身,而是函数、类和方法中的名字查找顺序。原文的核心结论很直接:局部变量通常只能在定义它的函数内部访问,全局变量可以在模块级直接访问;一旦函数内部对某个名字赋值,这个名字在函数内就会被当作局部变量处理,从而影响读取顺序和报错类型。下面按原文示例,从类变量访问、真正全局变量、同名优先级、UnboundLocalError 和 global 几个场景梳理。

一、类中的变量不能在方法里直接访问
原文先给出一个类变量示例。类中定义 a,方法中定义 b,如果方法里直接 print(a),编译器会报错,因为 a 是类变量,不是方法内的局部变量,也不是全局变量。错误写法:
  1. class variable:
  2.     a = '我是类变量'
  3.     def showvarible(self):
  4.         b = '我是函数变量'
  5.         print(a)
  6.         print(b)
  7. variable().showvarible()
复制代码
要访问类变量,可以用类名 variable.a,也可以通过 self.a。两种写法的结果一致:
  1. class variable:
  2.     a = '我是类变量'
  3.     def showvarible(self):
  4.         b = '我是函数变量'
  5.         print(variable.a)
  6.         print(b)
  7. variable().showvarible()
复制代码
  1. class variable:
  2.     a = '我是类变量'
  3.     def showvarible(self):
  4.         b = '我是函数变量'
  5.         print(self.a)
  6.         print(b)
  7. variable().showvarible()
复制代码
输出为:
我是类变量
我是函数变量

原文还演示了通过构造函数给实例属性赋值:
  1. class variable:
  2.     def __init__(self, a):
  3.         self.a = '我是类变量'
  4.     def showvarible(self):
  5.         b = '我是函数变量'
  6.         print(self.a)
  7.         print(b)
  8. variable(1).showvarible()
复制代码
这里需要注意,实例化时必须传入参数,因为 __init__ 定义了参数 a。Python 是动态语言,不需要指定参数类型,传 int 如 1 可以,传字符串也可以。这个例子里的 self.a 被赋值为字符串,但参数本身没有类型限制。

二、真正的全局变量和函数局部变量
原文给出真正的全局变量示例:a 定义在模块级,函数内部可以读取;b 定义在函数内部,属于局部变量:
  1. a = '我是真正的全局变量'
  2. def showvariable():
  3.     b = '我一直都是局部变量'
  4.     print(a)
  5.     print(b)
  6. showvariable()
复制代码
输出为:
我是真正的全局变量
我一直都是局部变量

如果试图在函数外访问函数内的局部变量 b,就会触发 NameError:
  1. a = '我是真正的全局变量'
  2. def showvariable():
  3.     b = '我一直都是局部变量'
  4.     print(a)
  5. def showb():
  6.     print(b)
  7. showvariable()
复制代码
典型报错是:
我是真正的全局变量
Traceback (most recent call last):
  File 'F:/leetcode/xxx.py', line 13, in <module>
    showb()
  File 'F:/leetcode/xxx.py', line 9, in showb
    print(b)
NameError: name 'b' is not defined
这说明 b 只属于 showvariable 的局部作用域,函数执行结束后不能在外部继续使用。

三、同名时局部变量优先,函数内赋值会导致 UnboundLocalError
当全局变量和函数内局部变量同名时,函数内部优先使用局部变量:
  1. a = '我是真正的全局变量'
  2. def showvariable():
  3.     a = '我一直都是局部变量'
  4.     print(a)
  5. print(a)
  6. showvariable()
复制代码
输出为:
我是真正的全局变量
我一直都是局部变量
也就是模块级先打印全局 a,调用 showvariable 后打印的是函数内局部 a。

更隐蔽的情况是函数内先读取、后赋值:
  1. a = 3
  2. def showvariable():
  3.     a = a * 5
  4.     print(a)
  5. showvariable()
复制代码
这段代码不会输出 15,而是抛出:
Traceback (most recent call last):
  ...
UnboundLocalError: local variable 'a' referenced before assignment
原因不是全局变量不能参与运算,而是函数内部出现了 a = ... 这样的赋值语句,Python 会把 a 视为局部变量。局部变量 a 在参与 a * 5 之前还没有被赋值,所以报 UnboundLocalError。

四、global 声明和赋值顺序
如果确实要在函数内部操作全局变量,需要用 global 声明:
  1. def showvariable():
  2.     global a
  3.     a = '我是global'
  4.     print(a)
  5. showvariable()
复制代码
输出:
我是global

声明后,这个 a 就能在函数外访问:
  1. def showvariable():
  2.     global a
  3.     a = '我是global'
  4.     print(a)
  5. def showglobal():
  6.     print(a)
  7. showvariable()
  8. showglobal()
复制代码
输出:
我是global
我是global

但 global 只是声明,不等于赋值。原文特别强调,必须赋值后才有意义。如果还没有执行赋值就去打印,仍然会报 NameError:
  1. def showvariable():
  2.     global a
  3.     a = '我是global'
  4.     print(a)
  5. def showglobal():
  6.     print(a)
  7. print(a)
  8. showvariable()
  9. showglobal()
复制代码
报错为:
Traceback (most recent call last):
  ...
NameError: name 'a' is not defined
因为此时 global a 还没有完成赋值,模块级 print(a) 访问不到已定义的 a。

五、排查与总结
从这些示例可以归纳出几条实用判断:局部变量只能在函数内部访问;全局变量可以在模块级直接访问,函数内默认可读;函数内对同名变量赋值时,会优先按局部变量处理;要在函数内修改全局变量,需要先写 global 声明,并确保赋值发生在读取之前;类变量不要直接当全局变量或局部变量使用,应通过类名或 self 访问;实例化带 __init__ 参数的类时,必须按形参传入实参。

遇到 NameError: name 'xxx' is not defined,先检查变量是否在当前位置的作用域内,以及 global 后是否已经赋值。遇到 UnboundLocalError: local variable 'xxx' referenced before assignment,通常要检查函数内部是否在后面写了赋值语句,导致该变量被判定为局部变量。把作用域和赋值顺序理清,这类 Python 变量问题就能定位。
回复

使用道具 举报

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

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-10-8 12:40 , Processed in 0.026431 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部