python中的内置globals()详解

最近在看一段代码时,发现作者使用globals()来应对无法确定变量名的情况,故此对该函数进行了详细的了解。

一、基本情况

globals() 是Python的标准内置函数,用于返回当前模块中定义的全局变量的字典(包含所有的全局变量、函数、类、导入的模块) ,其中键是变量名,值是变量的值。字典可进行修改,修改后会随之反映到全局变量上。对于函数内的代码,返回的为在定义函数时确定的该字典,无论在哪里调用都保持不变。

globals是特殊场景的解决方案,如动态批量处理全局变量/函数(eg:配置加载)、调试时快速排查全局状态问题、框架/库的底层实现,且有完善的封装和文档。
缺点:可读性差,命名冲突、作用域误区、线程不安全、性能损耗、破坏封装
重点:非必要不使用,优先使用字典/类进行替代,使用时做好校验、锁和注释。

二、常用场景实例
核心价值是动态操作全局命名空间 ,由于操作的是全局状态,使用时需要谨慎。

1、批量处理配置变量、根据配置变量名读取对应的值

python 复制代码
config0= 10
config1 = 30
config2 = "INFO"

# 变量名来自外部(如配置文件、用户输入)
config_keys = ["config0", "config1", "config2"]
# 动态读取所有配置
for key in config_keys:
    if key in globals():
        print(f"{key}: {globals()[key]}")

运行结果:

2、批量初始化

编码前,无法预知所有的变量名或需要生成大量相似的变量名,需要通过代码逻辑批量生成全局变量

3、函数内部修改全局变量

编码需要时,则需要使用,此处不进行举例说明了。

三、使用注意事项

globals()直接操作全局命名空间,滥用会导致代码可读性、可维护性大幅下降,甚至引发隐藏bug

1、严重降低代码可读性与可维护性

动态操作的变量/函数名是字符串,IDE无法自动补全、跳转,代码审查时难以追踪变量的定义/修改位置,后期维护成本高,如下图所示:

上方globals()里面变量/函数内容很多,某个变量随时可能被其他方法更改,当出现问题时,排查很困难。下方单独使用var_dict很清晰能知道变量是在哪里来的,在哪里修改的。

2、易引发命名冲突与意外覆盖

全局命名空间包含模块顶层定义的变量、函数、类等,globals()动态创建/修改变量,若名称与已有重复,则会静默覆盖原有对象。

在程序中,如不小心覆盖内置函数,在访问中可能会遇到报错,如下所示

在程序中,如多处动态给某个变量赋值,则会造成功能错误

如上所示,本身天津和重庆都有叫张三的,结果由于程序后面进行了覆盖,只保留了最后的值。这种在程序中也不存在语法错误,在排查bug时会比较麻烦
建议:动态命名时加前缀,避免与内置名、已有变量重名
操作赋值前,先校验是否为已有变量

3、globals()作用区域理解存在误区

python中的几种作用域可详见:https://blog.csdn.net/qq_44801116/article/details/158041678?spm=1001.2014.3001.5501

很多人会误以为globals()可以获取到外层函数作用域,实际是未搞清楚外层函数的变量的作用域为嵌套作用域,不会影响全局变量。嵌套函数想访问外层函数变量,需要使用nonlocal或local(),而非globals()

4、多线程时,存在线程安全问题

多线程同时通过globals()读写全局变量时,同时修改一个变量,会导致数据不一致。

python 复制代码
count = 0
def increment():
    for _ in range(100000):
        globals()["count"] += 1

t1 = threading.Thread(target=increment)
t2 = threading.Thread(target=increment)
t1.start()
t2.start()
t1.join()
t2.join()
print(count)

运行结果是200000,但是实际结果是随机,如下

可以通过加锁的形式,访问全局变量从而保证结果的正确性。

python 复制代码
def increment():
    for _ in range(100000):
        while threading.Lock():
            globals()["count"] += 1

5、性能损耗:尤其频繁操作

globals返回的字典,字典的键查找/赋值比直接访问变量要慢,尤其在频繁操作时会累积性能损耗。

建议循环中需要频繁操作局部变量时,先将赋值给局部变量,操作完成后再赋值给全局变量

相关推荐
Birdy_x21 分钟前
接口自动化项目实战(1):requests请求封装
开发语言·前端·python
我爱学习好爱好爱25 分钟前
Ansible 常用模块详解:lineinfile、replace、get_url实战
linux·python·ansible
海海不瞌睡(捏捏王子)37 分钟前
C++ 知识点概要
开发语言·c++
桌面运维家1 小时前
VLAN配置进阶:抑制广播风暴,提升网络效率
开发语言·网络·php
一轮弯弯的明月2 小时前
Python基础-速通秘籍(下)
开发语言·笔记·python·学习
西西学代码2 小时前
Flutter---回调函数
开发语言·javascript·flutter
大尚来也2 小时前
深入HashMap底层:从JDK1.7到1.8的架构演进与性能突围
开发语言
千寻girling3 小时前
面试官 : “ 说一下 Python 中的常用的 字符串和数组 的 方法有哪些 ? ”
人工智能·后端·python
森林里的程序猿猿3 小时前
并发设计模式
java·开发语言·jvm
222you3 小时前
四个主要的函数式接口
java·开发语言