发布时间:2026/6/23 2:59:32
写Python函数,什么时候该用全局变量?
免费编程软件「pythonpycharm」链接https://pan.quark.cn/s/48a86be2fdc0一个让我被同事喷了一顿的PR上周我提交了一个PR代码大概是这样的# config.py DEBUG True API_URL https://api.example.com TIMEOUT 30 MAX_RETRIES 3 # main.py import config def fetch_data(): url config.API_URL timeout config.TIMEOUT # ... 发送请求 if config.DEBUG: print(f请求URL: {url}) def process_result(data): for attempt in range(config.MAX_RETRIES): # ... 重试逻辑 if config.DEBUG: print(f重试第{attempt1}次)我用了好几个全局变量准确说是模块级变量。代码审查时同事发来一句话全局变量不是洪水猛兽但你也用得太多了一点。我愣住了。全局变量到底该不该用什么时候用是合理的什么时候是坏味道这个问题没有标准答案但有一些准则。今天我把这些准则整理出来帮你拿捏好用和不用之间的尺度。第一步先搞清楚我们说的是哪种全局变量在Python里全局变量这个词被用得很宽泛。实际上有三种不同的全局1. 真正的全局变量模块级变量写在.py文件顶层的变量整个模块都能访问。# settings.py APP_NAME 我的应用 # 这就是模块级变量 VERSION 1.02. 函数的全局变量用global声明的在函数内部通过global关键字修改的变量。counter 0 def increment(): global counter # 让函数可以修改模块级变量 counter 13. 跨模块共享的变量通过import在多个模块之间共享的变量。# state.py user_count 0 # module_a.py import state state.user_count 1 # module_b.py import state print(state.user_count) # 看到module_a的修改我们讨论的全局变量通常指的就是这三种情况。它们的共同点是生命周期贯穿整个程序运行期间多个函数或模块都能访问。第二步全局变量的优点——它确实有用在说缺点之前先承认一个事实全局变量不是原罪。有些场景用它代码反而更清晰。优点1配置信息天然适合用全局变量程序的配置参数API地址、超时时间、调试开关在运行期间基本不变而且很多地方都需要用到。把它们放在模块顶层所有函数都能方便地访问。# 这些放在模块顶层清晰且方便 DATABASE_URL postgresql://localhost:5432/mydb LOG_LEVEL INFO ENABLE_CACHE True如果用对象来封装反而会增加不必要的复杂度。优点2缓存和单例状态需要持久化有些数据需要在多次调用之间保持比如缓存字典、连接池、全局计数器。# 缓存结果避免重复计算 _cache {} def get_user(user_id): if user_id not in _cache: _cache[user_id] fetch_from_db(user_id) return _cache[user_id]这种全局缓存是合理的——它确实需要在全局范围内存在。优点3减少参数传递的噪音如果一个变量被很多层函数调用传递而且每个函数都只是路过它那么用全局变量可以减少参数噪音。# 不用全局每一层都要传递debug def level3(debug): if debug: print(level3) def level2(debug): level3(debug) def level1(debug): level2(debug) # 用全局只在一处设置到处可用 DEBUG True def level3(): if DEBUG: print(level3)当然这种便利性是有代价的后面会说。第三步全局变量的缺点——它确实有风险缺点1隐式依赖降低可读性看这个函数def calculate_discount(price): if DISCOUNT_RATE 0.5: # DISCOUNT_RATE从哪来的 return price * 0.8 return price * 0.9读代码的人得去模块顶部找DISCOUNT_RATE在哪里定义的。如果这个文件有200行这增加了认知负担。相比之下参数传递是显式的def calculate_discount(price, discount_rate): if discount_rate 0.5: return price * 0.8 return price * 0.9一眼就知道这个函数依赖了哪些输入。缺点2全局状态让测试变困难写单元测试时全局变量是噩梦。因为测试之间会互相影响。counter 0 def increment(): global counter counter 1 return counter # test1 assert increment() 1 # test2 assert increment() 1 # 会失败因为counter现在是2要让测试独立你不得不在每个测试前重置全局变量。这很麻烦也容易遗漏。缺点3并发问题如果你的程序用了多线程全局变量需要加锁保护否则会出现数据竞争。counter 0 def increment(): global counter counter 1 # 这不是原子操作多线程下会出问题多个线程同时执行这行代码可能得到错误的结果。需要用threading.Lock来保护。缺点4代码耦合难以重构全局变量像一条无形的线把很多函数串在一起。当你想要修改它的行为时需要检查所有用到它的地方。比如把DEBUG从布尔值改成字符串级别DEBUG、INFO、WARNING所有用到它的函数都得改。第四步什么时候该用——一个决策框架不要简单地禁止全局变量而是根据场景判断。我整理了一个决策树场景1配置类变量→ ✅ 可以放心用程序运行期间基本不变多个地方需要访问。# 合理 API_BASE_URL https://api.example.com/v1 MAX_CONNECTIONS 10 DEFAULT_TIMEOUT 30场景2缓存和共享状态→ ⚠️ 谨慎使用用之前考虑有没有更好的方式# 合理缓存查询结果 _user_cache {} def get_user(id): if id not in _user_cache: _user_cache[id] db.query(id) return _user_cache[id] # 但如果是复杂的状态管理考虑用类封装场景3函数间传递的临时状态→ ❌ 用参数代替如果某个值只是在几个函数之间传递应该用参数不要用全局变量。# 不推荐 current_user None def set_user(user): global current_user current_user user def get_user(): return current_user # 推荐 def process_user(user): # 直接传递user参数 validate(user) save(user)场景4常量值→ ✅ 可以放心用数学常量、枚举值、固定字符串。# 合理 PI 3.1415926 MAX_UPLOAD_SIZE 10 * 1024 * 1024 # 10MB DEFAULT_PAGE_SIZE 20按惯例常量用全大写命名。第五步不用全局变量用什么呢如果你决定减少全局变量有几种替代方案替代1封装成类把相关的状态和行为放在一个类里。# 全局变量方式 counter 0 def increment(): global counter counter 1 # 类封装方式 class Counter: def __init__(self): self.count 0 def increment(self): self.count 1 # 使用 c Counter() c.increment()当状态和行为绑定在一起时类比全局变量更清晰。替代2用函数参数传递# 不推荐 DEBUG True def log(message): if DEBUG: print(f[DEBUG] {message}) # 推荐 def log(message, debugFalse): if debug: print(f[DEBUG] {message}) # 调用时显式传递 log(启动完成, debugTrue)替代3用配置对象如果有大量配置参数用一个配置对象来集中管理。# 不推荐散落的全局变量 DB_HOST localhost DB_PORT 5432 DB_NAME mydb DB_USER admin DB_PASS secret def connect(): return connect_to_db(DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASS) # 推荐配置对象 class Config: def __init__(self): self.db_host localhost self.db_port 5432 self.db_name mydb self.db_user admin self.db_pass secret config Config() def connect(): return connect_to_db( config.db_host, config.db_port, config.db_name, config.db_user, config.db_pass )如果需要还可以从配置文件或环境变量加载配置。替代4用模块级别的单例Python的模块本身就是单例。如果你需要全局唯一的状态放在模块里是合理的。# state.py _data {} def set(key, value): _data[key] value def get(key): return _data.get(key) # 其他模块导入state使用这实际上还是用了全局变量但通过函数接口封装了访问方式比直接暴露_data要好。第六步几个真实案例案例1Web应用的配置Flask、Django等Web框架都大量使用全局配置。这不是问题因为配置在运行时基本不变。# Django的settings.py SECRET_KEY ... DATABASES {...} INSTALLED_APPS [...]✅合理。配置变量就是全局的这就是它们的设计目的。案例2日志记录器import logging logger logging.getLogger(__name__) def do_something(): logger.info(doing something)✅合理。日志记录器在整个应用中是唯一的用全局变量访问非常方便。案例3数据库连接池connection_pool create_pool() def get_connection(): return connection_pool.get_connection()⚠️需要谨慎。连接池作为全局单例是常见的但考虑一下如果将来需要多个数据库连接池怎么办用类封装会更灵活。案例4用户会话信息current_user None def set_user(user): global current_user current_user user def show_profile(): print(current_user.name)❌不推荐。用户信息是请求级别的状态不应该用全局变量存储。Web框架通常用request对象来携带用户信息。第七步一个参考标准——全局变量的3个问题在决定是否使用全局变量前问自己三个问题问题1这个变量在程序运行期间会变化吗不会常量→ ✅ 放心用会但变化极少配置→ ✅ 可以接受会频繁变化状态→ ⚠️ 考虑用类封装问题2有多少个地方会访问这个变量1-2个函数 → 考虑用参数传递3-5个函数 → 可以考虑用全局5个以上 → 考虑用配置对象或类问题3这个变量的用途是什么配置/常量 → ✅ 适合全局缓存 → ⚠️ 谨慎确保有清理策略临时状态 → ❌ 避免全局单例服务 → ⚠️ 考虑用依赖注入第八步代码中的信号如果一段代码中出现了以下信号说明全局变量可能用得太多了一个模块里有超过5个global声明函数里有超过3个global声明全局变量被修改的地方超过10处测试文件里需要setUp和tearDown重置一堆全局变量你无法在同一个进程里运行两次不同的测试状态冲突当你看到这些信号时考虑重构。一张决策表使用场景是否推荐原因推荐做法常量PI、MAX_SIZE✅ 推荐不变到处用全大写命名配置API_URL、DEBUG✅ 推荐基本不变集中管理放在settings模块缓存查询结果⚠️ 谨慎有用但容易失控用类封装提供清理方法单例服务logger✅ 推荐全局唯一方便访问模块级变量跨函数传递的临时状态❌ 避免增加隐式依赖用参数传递多线程共享状态⚠️ 谨慎需要锁保护用线程安全的数据结构用户会话信息❌ 避免请求级别不是全局级别用上下文变量或request对象回到开头的那个PR我那个PR被同事喷了之后我做了这样几件事区分了配置和状态DEBUG、API_URL、TIMEOUT是配置保留在模块顶层封装了需要变化的状态MAX_RETRIES其实在不同的场景下不同改成了参数传递移除了不必要的全局有些函数里用全局变量只为了少传一个参数我都改成了显式传递改完之后代码变长了但变清晰了。审查通过了。最后一句总结全局变量不是不能用而是要知道什么时候用、什么时候不该用。一个简单的判断标准如果这个变量是属性属于程序本身的配置或常量可以用全局如果这个变量是状态描述程序运行过程中的变化尽量别用全局。换句话说配置用全局状态用对象。记住这句话下次写全局变量的时候你会多思考三秒钟。这三秒钟可能就是好代码和坏代码的区别。

相关新闻

第4节:我应该选择哪种Kafka?
2026/6/23 2:59:32

第4节:我应该选择哪种Kafka?

Kafka不再是一个单纯的消息引擎系统,而是能够实现精确一次(Exactly-once)处理语义的实时流处理平台。Apache KafkaApache Kafka是最“正宗”的Kafka,也应该是你最熟悉的发行版了。自Kafka开源伊始,它便在Apache基金会孵…

阅读更多
029、自定义命令开发:创建、参数化、共享与团队复用的最佳实践
2026/6/23 2:59:32

029、自定义命令开发:创建、参数化、共享与团队复用的最佳实践

029、自定义命令开发:创建、参数化、共享与团队复用的最佳实践上周五晚上十一点,我盯着终端里那条报错信息看了整整十五分钟——“Command not found: deploy-staging”。明明刚用CodeX的codex command create注册了自定义命令,重启终端后却死…

阅读更多
5分钟快速掌握:用Mermaid Live Editor让技术图表创作变得如此简单
2026/6/23 2:59:32

5分钟快速掌握:用Mermaid Live Editor让技术图表创作变得如此简单

5分钟快速掌握:用Mermaid Live Editor让技术图表创作变得如此简单 【免费下载链接】mermaid-live-editor Edit, preview and share mermaid charts/diagrams. New implementation of the live editor. 项目地址: https://gitcode.com/GitHub_Trending/me/mermaid-…

阅读更多
iPaaS核心功能系列(二)| 数据映射与转换——让不同系统“说同一种话”
2026/6/23 3:59:32

iPaaS核心功能系列(二)| 数据映射与转换——让不同系统“说同一种话”

一、系统接上了,但还是“听不懂”在第一篇文章中,我们讨论了iPaaS如何通过连接器把不同的系统“接”到一起。但“接上”并不等于“能沟通”——就像你把一个只会说中文的人和一群说英语的人放在同一个会议室里,物理上他们在一起了&#xff0c…

阅读更多
缓存详细设计
2026/6/23 3:59:32

缓存详细设计

缓存详细设计 1. 概述 系统使用 Redis 作为分布式缓存,通过 StringRedisTemplate 进行操作。缓存服务统一放置在 dp-service/src/main/java/com/cms/service/cache/ 目录下,按业务域划分为三个独立服务: 服务类 前缀 用途 AdminCacheService dp:admin:v1: 管理端权限缓存 …

阅读更多
618为何不再疯狂打价格战?答案全在这份报告——2026年“618”大促全周期观察
2026/6/23 3:59:32

618为何不再疯狂打价格战?答案全在这份报告——2026年“618”大促全周期观察

易观分析:2026 年 618 大促全新趋势来袭——简化优惠规则已达成行业共识,整体市场保持稳健增长。同时,大促中也呈现出一些新亮点:商家摆脱价格极致内卷,回归产品力与原创设计,经营重心从冲量转向利润优化&a…

阅读更多
豆包在抖音生态中的实战应用场景大纲
2026/6/23 3:59:32

豆包在抖音生态中的实战应用场景大纲

做短视频和直播运营的朋友,最近应该都有同感:内容创作的节奏越来越快,单纯靠人力去堆脚本、盯评论、复盘数据,不仅效率低,还容易在高压下动作变形。以前一个团队一天能打磨出三条精品视频就算高产,现在面对…

阅读更多
吵架后焦虑失眠:助眠工具治标,源头是没和好的关系
2026/6/23 3:59:32

吵架后焦虑失眠:助眠工具治标,源头是没和好的关系

那种吵完架躺床上反复回放的失眠,助眠 App 治不了。 这种失眠,吃褪黑素没用,听白噪音也没用。因为折磨你的根本不是"睡不着"这个生理问题,是那件没解决的事、那个没和好的人,堵在心里。 我有段时间总搜"…

阅读更多
写Python函数,什么时候该用全局变量?
2026/6/23 2:59:32

写Python函数,什么时候该用全局变量?

免费编程软件「pythonpycharm」 链接:https://pan.quark.cn/s/48a86be2fdc0一个让我被同事喷了一顿的PR上周我提交了一个PR,代码大概是这样的:# config.py DEBUG True API_URL "https://api.example.com" TIMEOUT 30 MAX_RETRIE…

阅读更多
嵌入式语音编解码实战:G.726 ADPCM库集成与优化指南
2026/6/23 3:25:21

嵌入式语音编解码实战:G.726 ADPCM库集成与优化指南

1. 项目概述与G.726 ADPCM技术背景在嵌入式语音处理领域,带宽和存储资源往往是寸土寸金的。如果你做过对讲机、VoIP网关或者早期的数字录音设备,一定对如何在有限的比特率下保住语音可懂度这件事深有感触。我当年接手一个车载调度系统的项目,…

阅读更多
ITU656格式化器寄存器配置实战:VBI数据处理与VCR特技播放兼容性
2026/6/21 0:59:13

ITU656格式化器寄存器配置实战:VBI数据处理与VCR特技播放兼容性

1. 项目概述与核心挑战在数字视频处理领域,将原始的视频数据、同步时序以及各种辅助信息打包成一个标准、稳定的串行数据流,是确保设备间互联互通的基础。ITU-R BT.656标准(常简称为ITU656)正是为此而生的一套“交通规则”。它定义…

阅读更多
嵌入式GUI开发实战:emWin环境搭建、配置优化与性能调优指南
2026/6/23 0:40:11

嵌入式GUI开发实战:emWin环境搭建、配置优化与性能调优指南

1. 项目概述与emWin核心价值解析在嵌入式系统开发领域,人机交互(HMI)的设计正从简单的LED指示灯和按键,快速向全彩图形化界面演进。无论是智能家电上的触摸屏、工业PLC的操作面板,还是医疗设备的参数显示,一…

阅读更多
3分钟快速上手:Qwen3大语言模型本地部署完全指南
2026/6/23 0:59:31

3分钟快速上手:Qwen3大语言模型本地部署完全指南

3分钟快速上手:Qwen3大语言模型本地部署完全指南 【免费下载链接】Qwen1.5 Qwen3 is the large language model series developed by Qwen team, Alibaba Cloud. 项目地址: https://gitcode.com/GitHub_Trending/qw/Qwen1.5 阿里巴巴Qwen3大语言模型系列以其…

阅读更多
微信聊天记录备份指南:使用WeChatExporter轻松保存您的珍贵回忆
2026/6/23 0:59:31

微信聊天记录备份指南:使用WeChatExporter轻松保存您的珍贵回忆

微信聊天记录备份指南:使用WeChatExporter轻松保存您的珍贵回忆 【免费下载链接】WeChatExporter 一个可以快速导出、查看你的微信聊天记录的工具 项目地址: https://gitcode.com/gh_mirrors/wec/WeChatExporter 在数字时代,微信聊天记录承载着我…

阅读更多
Cortex-M指令集深度解析:饱和运算、位域操作与分支控制实战
2026/6/23 0:59:31

Cortex-M指令集深度解析:饱和运算、位域操作与分支控制实战

1. 从指令到效率:为什么Cortex-M指令集值得深挖如果你在嵌入式领域摸爬滚打了一段时间,尤其是跟ARM Cortex-M系列单片机打交道,那你肯定对“写寄存器”、“调库函数”这套流程熟得不能再熟了。但不知道你有没有过这样的感觉:项目代…

阅读更多
GIT修改用户名
2026/6/22 5:10:42

GIT修改用户名

在GIT中修改用户名可按以下步骤操作: 查看当前git的用户名,使用命令git config --list或git config user.name。修改git用户名,使用命令git config --global user.name "xxx(新的用户名)",将其中…

阅读更多
Win11Debloat:让你的Windows系统重获新生的终极优化工具
2026/6/22 10:07:50

Win11Debloat:让你的Windows系统重获新生的终极优化工具

Win11Debloat:让你的Windows系统重获新生的终极优化工具 【免费下载链接】Win11Debloat A simple, lightweight PowerShell script that allows you to remove pre-installed apps, disable telemetry, as well as perform various other changes to declutter and …

阅读更多
技术深度解析:m4s-converter实现原理与B站缓存视频转换最佳实践
2026/6/21 13:29:25

技术深度解析:m4s-converter实现原理与B站缓存视频转换最佳实践

技术深度解析:m4s-converter实现原理与B站缓存视频转换最佳实践 【免费下载链接】m4s-converter 一个跨平台小工具,将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter m4s-converter是一个…

阅读更多