摘要
- global 标志实际上是为了提示 python 解释器,表明被其修饰的变量是全局变量。这样解释器就可以从当前空间 (current scope) 中读写相应变量了。
- Python 的全局变量是模块 (module) 级别的
- 每个 python 函数拥有对应的 __globals__ 字典,该字典与函数所属模块的 __dict__ 字典完全相同。函数的全局变量也会从这个字典中获取
- 注:上面三句话的意思就是,python 解释器发现函数中的某个变量被 global 关键字修饰,就去函数的 __globals__ 字典变量中寻找(因为 python 中函数也是一等对象);同时,一个模块中每个函数的 __globals__ 字典变量都是模块 __dict__ 字典变量的引用,二者值完全相同。
- 避免全局变量将使得程序更容易被调试,同时也能提升程序的可读性
- 使用到的全局变量只是作为引用,不在函数中修改它的值的话,不需要加global关键字. 使用到的全局变量,需要在函数中修改的话,就涉及到歧义问题. 因此在函数中修改全局变量的话需要加global关键字
动机
我最近遇到了一个关于 python 全局变量的问题,如下面这个简单例子里展示(当然实际代码要比这个复杂的多,这里只是一个抽象出来当例子)。例子中 foo.py 定义了函数 f,而函数 f 调用了全局变量 a:
# foo.py def f(): print(a) def main(): global a a = 5 f() if __name__ == '__main__': main()
运行上面这个文件将如预料中的输出5。在另一个文件 bar.py 中我们引入上面的 f,代码如下
# bar.py from foo import f def main(): f() main()
运行 bar.py 将报 NameError 错误。这是因为 a 被定义在 foo.py 的 main 函数中,而当导入 f 函数时, foo.py 的 main 函数并未被运行,所以 a 也没哟被定义。
Traceback (most recent call last): File "bar.py", line 10, in <module> main() File "bar.py", line 7, in main f() File "foo.py", line 5, in f print(a) NameError: global name 'a' is not defined
定义全局变量 a
为了修复上面当问题第一反应是在 bar.py 中定义全局变量 a,这样 f 就可以找到变量 a 了,如下面的代码:
# bar.py from foo import f def main(): global a a = 4 f() main()
然而依旧会报错,黑人问号脸"htmlcode">
Traceback (most recent call last): File "/tmp/example/bar.py", line 13, in <module> main() File "/tmp/example/bar.py", line 9, in main f() File "/tmp/example/foo.py", line 5, in f print(a) NameError: global name 'a' is not defined
函数的 __globals__ 属性与 python 的 global 语句
python 的 global 语句的作用只是提示 python 解释器,被 global 修饰的变量是一个全局变量,利用上面例子里函数 f 的反编译代码可以清除的看到这一点:
import dis from foo import f dis.dis(f)
5 0 LOAD_GLOBAL 0 (print) 2 LOAD_GLOBAL 1 (a) 4 CALL_FUNCTION 1 6 POP_TOP 8 LOAD_CONST 0 (None) 10 RETURN_VALUE
面可以看出变量 a 被认为是全局变量。Python 中的每一个函数都拥有一个 __globals__ 字典变量,该变量实际是函数所属模块的 __dict__ 变量的引用。所以在 bar.py 中我们想在 bar.main 函数中将全局变量 a 赋值为4,实际改变的是 bar.py 的 __dict__ 字典变量 (注:而不是定义 f 的 foo.py 的 __dict__ 字典变量)
# bar.py def main(): global a a = 4 print(main.__globals__.keys()) print(main.__globals__['a']) dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'foo', 'f', 'dis', 'main', 'a']) 4
上面的代码输出了 main.__globals__ (即 bar.__dict__ ) 中全局变量 a 的值是4,然而这个值对函数 f 来说确是不可见的,因为 f.__globals__ 实际等于 foo.__dict__ (简单而言就是命名空间不同)
from foo import f print(f.__globals__)
假设我们在 foo.py 所有函数的外部预先定义了全局变量 a ,那么在将函数 f 导入时,a 会随着 f.__globals__ 一同被导入。但这时被导入的 f.__globals__["a"] ( 即 foo.__dict__["a"] ) 和 bar.main 中赋值的 bar.main.__globals__["a"] ( 即 bar.__dict__["a"] ) 仍然不是同一个变量,即赋值无法改变函数 f 的输出,如下面的例子所示。
# foo.py a = 3 def f(): print(a) def main(): global a a = 5 f() if __name__ == '__main__': main()
# bar.py from foo import f def main(): global a a = 4 f() main()
运行 bar.py 输出3,而不是 4。
修改函数全局变量的值:更新 globals
就上述例子而言,如果我们想在 bar.py 中改变函数 f 的输出,则需要直接更新其 __globals__ 变量的值。
# bar.py from foo import f def main(): f.__globals__['a'] = 4 f() main()
- 模块的 dict 变量和猴子布丁 (monkey-patching)
如上所述,函数的 __globals__ 变量实际是其所属模块 __dict__ 变量的引用。所以为了达到上面修改全局变量的目的,也可以直接更新 foo.__dict__ 。修改模块 foo 的属性 (attribute) 值即可直接更新 foo.__dict__ 。
# bar.py import foo from foo import f def main(): foo.a = 4 f()
如果你曾经使用过运行中给代码打补丁的库,一般就是这么实现的。直接修改被打补丁的模块的 __dict__ 中特定的对象或函数。、
输入使得函数变得更加容易测试
上面的例子中的函数 f 如果接受输入变量的话,而不是使用全局变量,代码将更容易被测试。同时可读性也更好,出了问题也更容易 debug。
# foo.py def f(a): print(a) def main(): a = 5 f(a) if __name__ == '__main__': main()
# bar.py from foo import f def main(): a = 3 f(a)
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
python,global关键字
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 群星《前途海量 电影原声专辑》[FLAC/分轨][227.78MB]
- 张信哲.1992-知道新曲与精丫巨石】【WAV+CUE】
- 王翠玲.1995-ANGEL【新艺宝】【WAV+CUE】
- 景冈山.1996-我的眼里只有你【大地唱片】【WAV+CUE】
- 群星《八戒 电影原声带》[320K/MP3][188.97MB]
- 群星《我的阿勒泰 影视原声带》[320K/MP3][139.47MB]
- 纪钧瀚《胎教古典音乐 钢琴与大提琴的沉浸时光》[320K/MP3][148.91MB]
- 刘雅丽.2001-丽花皇后·EMI精选王【EMI百代】【FLAC分轨】
- 齐秦.1994-黄金十年1981-1990CHINA.TOUR.LIVE精丫上华】【WAV+CUE】
- 群星.2008-本色·百代音乐人创作专辑【EMI百代】【WAV+CUE】
- 群星.2001-同步过冬AVCD【环球】【WAV+CUE】
- 群星.2020-同步过冬2020冀待晴空【环球】【WAV+CUE】
- 沈雁.1986-四季(2012梦田复刻版)【白云唱片】【WAV+CUE】
- 纪钧瀚《胎教古典音乐 钢琴与大提琴的沉浸时光》[FLAC/分轨][257.88MB]
- 《国语老歌 怀旧篇 3CD》[WAV/分轨][1.6GB]