Python 现代开发学习笔记

(一)沿 traceback 找到失败的动作

1. 先读错误,再还原调用过程

先保存这份错误示例。任务是把每行的分钟数相加,第二行却写成了“很多”:

# 项目文件:debug_demo/trace_failure.py
# 错误示例:第二行不能转成整数,用它观察异常从哪里产生、经过哪些调用。
def parse_minutes(text):
    return int(text)


def total_minutes(lines):
    total = 0
    for line in lines:
        total += parse_minutes(line)
    return total


def main():
    print(total_minutes(["20", "很多", "45"]))


if __name__ == "__main__":
    main()
# 在 debug_demo 目录运行,让解释器显示未被接住的异常。
python trace_failure.py

最后一行是 ValueError: invalid literal for int() with base 10: '很多'。先读出“整数转换失败”和实际值 '很多',再向上找到 return int(text)。这里是产生错误的动作;继续向上,可以看到它由 total_minutes 的加法调用,再由 main 发起。traceback 从较外层的调用列到较内层,最后给出异常类型和说明。

当第二行失败时,total 已经是 20,但这一轮的加法尚未完成,第三行也没有读取。把注意力放在 print 或总和初值上,不能解释这次转换失败。

从错误值向外还原转换、累计与入口的调用关系

上一章的 raise RecordError(...) from error 会同时保留“第几条记录错误”和底层转换原因。看到异常链时,先定位各段最后的类型与说明,再把它们连成“哪个业务动作调用了哪个底层动作”。

2. 保留一次可重复运行的失败输入

排查文件导入时,先记下启动命令、解释器版本、输入文件和实际结果。输入里有很多书,可以从副本中逐步删掉无关记录;删完后仍出现同一个错误,剩下的内容就更容易逐项观察。

本例从三行缩成 ["很多"],仍然在 int 处失败。需要的是数字时,修正数据;需要向读者说明哪一行错误时,沿用第五章的记录检查和异常链。把它改成 except Exception: pass 会跳过这条记录,却不能完成“统计所有有效输入并反馈坏记录”的任务。

记录失败时还要区分结果:程序在什么动作抛出异常;程序正常结束却给出错误答案;程序一直等待;同一输入两次得到不同结果。后面几种情况没有现成的错误行,需要检查执行中的状态。

笔记正文与全部题解 · Ctrl / ⌘ K