第二章:print调试法——最朴素的调试利器
说实话,很多新手觉得print调试太low了。他们一上来就想着用IDE断点、用各种高端工具。但我得说句公道话——print调试法,是每个程序员都应该掌握的看家本领。
我在项目中遇到过好几次这样的情况:生产环境出了问题,断点打不上去,日志级别又不对。这时候怎么办?靠的就是print。说白了,它就像医生的听诊器,简单但管用。
2.1 print语句的妙用
很多人觉得print就是往控制台扔几个变量值。嗯,这没错,但太粗糙了。我个人的习惯是,把print当成一种「轻量级埋点」。
举个例子,你怀疑某个循环有问题:
for i in range(1000):
result = process(i)
# 这里加个进度提示
if i % 100 == 0:
print(f"[进度] 已处理 {i}/1000 条")
# 关键路径上的数据
if result is None:
print(f"[异常] 第 {i} 条数据返回空")
你看,这样打印出来的信息,一眼就能看出问题在哪。而不是满屏的数字乱飞。
2.2 格式化输出调试信息
光会print还不够,你得会「优雅地print」。我见过太多人这样写:
print(a)
print(b)
print(c)
然后控制台里一堆数字,根本分不清谁是谁。你想想看,这跟没调试有什么区别?
我建议用f-string或者format来格式化输出:
# 推荐的做法
user_id = 1024
user_name = "张三"
balance = 99.99
print(f"[用户信息] id={user_id}, name={user_name}, balance={balance:.2f}")
# 或者用占位符
print("[调试] 用户 %s (ID: %d) 余额: %.2f" % (user_name, user_id, balance))
为什么要格式化?因为可读性就是生产力。你调试的时候,一眼就能看出每个字段的值,而不是对着屏幕数数字。
| 输出方式 | 可读性 | 适用场景 |
|---|---|---|
| 裸print | 差 | 临时看一眼 |
| f-string | 优 | 日常调试首选 |
| format | 良 | 需要对齐时 |
| JSON序列化 | 优 | 复杂数据结构 |
核心原则: 调试输出要让人「一眼看懂」,而不是「仔细分析」。
2.3 条件打印与调试开关
这是print调试法的进阶技巧。你不可能把所有print都留在代码里,那会变成噪音。我个人的做法是——加一个调试开关。
# 调试开关
DEBUG = True # 上线前改成 False
def debug_print(msg):
if DEBUG:
print(f"[DEBUG] {msg}")
# 使用
debug_print(f"用户登录: {user_id}")
debug_print(f"数据库查询耗时: {elapsed}ms")
这样,上线的时候只需要改一个变量,所有调试输出就都消失了。干净利落。
更高级一点,你可以用环境变量来控制:
import os
DEBUG = os.environ.get("APP_DEBUG", "0") == "1"
def debug_print(msg, level="INFO"):
if not DEBUG:
return
print(f"[{level}] {msg}")
# 使用
debug_print("开始处理请求", "INFO")
debug_print(f"请求参数: {params}", "TRACE")
2.4 条件打印的妙用
有时候你不想打印所有信息,只想看特定条件下的数据。这时候条件打印就派上用场了。
# 只在错误发生时打印
for item in data_list:
result = process(item)
if result is None:
print(f"[ERROR] 处理失败: item={item}, 上下文={context}")
# 只在特定阈值触发时打印
for i in range(10000):
if i % 1000 == 0:
print(f"[进度] 已完成 {i}/10000")
你想想看,如果每一条都打印,控制台早就刷屏了。但加上条件,你就能精准地抓到关键信息。
2.5 实战中的print调试套路
我总结了一套自己的print调试流程,分享给你:
- 定位问题区域:先确定大概哪个函数或循环出了问题
- 加标记性print:在入口和出口各加一个,确认执行路径
- 加关键变量print:把怀疑的变量值打出来
- 加条件print:只在异常条件下打印,减少噪音
- 分析并修复:根据输出定位问题
- 清理print:修复后记得删掉调试代码
2.6 print调试的局限性
当然,print不是万能的。我得实话实说:
- 多线程环境下,print输出会交错,很难看
- 性能敏感的场景,print会拖慢速度
- 无法动态修改调试点,得改代码重新跑
- 不适合调试GUI程序或嵌入式系统
但话说回来,对于80%的日常调试场景,print完全够用。别小看它,用好了就是一把利器。
嗯,这一章就到这里。记住,工具没有高低之分,只有会不会用。print调试法,值得你花时间练好。