只报错不报位置?用traceback.format_exc()才是真救星)
记录一个让我头疼了很久的话题 的 模块。启初之际, 我的针对异常的处置相当简易, 即为 as e: print(e) 这般。此情形能够将出现的差错告知于我, 像是“某个 key寻觅不见”亦或是“无法实施除以零此项操作”这类描述传达的差错样态便可得知。然一旦项目朝着愈发繁杂的方向发展, 构成函数调用形成的链条达至诸如七、八层深度的程度之时, 仅仅知悉“出现了何事”这一点自是远远不足够实现知晓来解决的目的, 我更为期望弄清楚的是——“它确切是在什么方位所产生出来的呢? ”。这一刻, 模块摇身一变成为了我的救星之作。然而, 转瞬之间, 我便遭遇到了全新的棘手难题:.() 以及 .() 这两位仿若孖宝一般的存在, 其模样简直过于逼近相似度极高, 以至于使得我频繁地陷入混淆之乱局, 根本无从知晓当运用两者之中的哪一个才是恰当之举。如今, 将我的学习所得体会记录一番, 力求一次性把它们完全透彻弄清楚。核心难题 vs可以用一个很形象的比喻来理解它俩案发现场。.(): 案发现场的“快照摄影师”这个被叫做“摄影师”的人, 有着这么一个特性, 那就是, 只有在案发现场, 即在那个区域内部, 才能够展开工作。它无需你给出任何线索, 一旦你予以调用, 便会即刻针对当前的场所咔嚓拍摄一张完整的快照, 该快照拥有整套的细节, 这些细节包括调用栈、错误类型以及错误信息, 随后它把相关内容整理成一张冗长的照片, 也就是一个字符串, 接着传递给予你之后。import traceback def main_func(): try: # 模拟一个深层调用 level1() except Exception as e: # 我们正处于“案发现场” print(--- 使用 format_exc ---) # “摄影师”出动自动拍照 error_info traceback.format_exc() print(error_info) # with open(error.log, a) as f: # f.write(error_info) def level1(): level2() def level2(): 1 / 0 main_func()人处于现场直接进行调用, 从而获得包含全部信息的错误报告字符串, 这般做法简单又粗暴, 然而却极为有效。.(e): 实验室里的“法证分析员”这位不一样的“分析员”, 他无需亲自去到现场, 工作方式是这样的: 你把现场收集的, 称作“证据包”的异常对象 e 带给他, 在任何地方, 其即在自身实验室就能帮你分析出完整的案情报告。import traceback # 这是一个专门处理错误的“法证实验室” def error_lab(evidence_bag): print(--- 使用 format_exception ---) # “分析员”开始工作分析你给的“证据包” # 它返回的是一个列表每一行是列表的一个元素 error_lines traceback.format_exception(evidence_bag) # 我们通常需要把它合并成一个字符串 error_info .join(error_lines) print(error_info) def main_func(): try: level1() except Exception as e: # 在现场捕获到了异常打包成“证据包” e # 然后把证据送到“实验室”去分析 error_lab(e) # level1 和 level2 函数同上 def level1(): level2() def level2(): 1 / 0 main_func()将异常对象 e 当作参数传来传去, 于任何想要处理的地方, 就在哪里去调用它如此更为灵活, 且更适宜代码分离。对比()(e)工作地点必须在 块内任何地方所需材料无自动获取必须提供异常对象 e返回类型字符串字符串列表比喻现场快照摄影师实验室法证分析员这会儿, 是不是能够将它俩区分开来了呀, 简要讲就是, 要是打算在其中当场予以处置的话那就采用, 要是想着让异常来来去去辗转传递之后再进行处理的话那就运用。工具箱里还有啥弄明白了上头那两个, 接着来瞧瞧, 模块当中其他的几个, 常用并且有用的工具。1. 系列懒人福音直接打印返回字符串的是系列, 得由我们自己去决定如何处理才行。而系列更为呈现得直接些, 它会径直帮你来把那些需要呈露并打印到控制台当中的信息, 其默认状态是 sys., 所以通常展现出来并且显示为色泽红盈盈似的。在你仅仅是想要快速地调试一番, 瞅一瞅那错误信息之际, 运用 () 是极为便利的。2. 系列专业工具获取结构化数据有的时候, 并非单纯只是只想去记录日志, 或许还会想要针对调用栈展开分析, 举例来说要统计哪一个文件、哪一个函数是最容易出现错误出现情况的那一种情况。在这个时候, 存在着一个大字符串这种情况就会致使处理存在不便的那种明显不利的情形了。异常对象 e 里面究竟有啥不间断地宣称要将异常对象 e 反复传递, 那么这个 e 内部究竟存有何种有价值之物呢?快速定位异常的几个技巧光会用工具还不够高效地找到问题才最重要。技巧一从下往上读信息看上去蛮长的哩, 然而阅读时候其顺序可是至关重要的。特别要从最底下的那一行起始阅读。处于百分之九十的情形下的时候, 你所需要做的仅仅是去查看最下面那两三行而已, 便能够定位那问题的根源之处了是这般而对, 可没错。技巧二用 模块让它帮你处理在进行生产的环境当中, 我们并非会去使用print, 而是会选用模块来开展日志的记录, 模块和是天生意义上一对的存在。import logging logging.basicConfig(levellogging.INFO, filenameapp.log, format%(asctime)s - %(levelname)s - %(message)s) try: 1 / 0 except Exception as e: # 关键在这里设置 exc_infoTrue logging.error(计算时发生了一个错误, exc_infoTrue)当你运用“.”加上“error”或者“.,.”等等之类的情况, 并且把“”设定为“True”的时候, 模块就会自动去进行调用, 然后会把完整的相关信息添加到你的日志消息的后面, 并以此作为附加内容。这相较于我们依靠手动去调用那个点括号, 紧接着再把它传递给别的什么, 可要简便且标准许多了, 这可是被大力推荐的一种做法呀技巧三绝不写 : pass 这样的“沉默代码”有时候我们觉得某个错误不重要就想忽略它。# 绝对不要这么做 try: # ... 一些可能会出错的代码 ... except: pass这等同于在犯罪现场将所有证据一概销毁, 并且未曾留存任何记录。程序不至于崩溃, 然而问题会以一种更使人感觉诡异的方式在未来的某个时刻展现显现出来, 到那时要想寻找到源头就艰难如同登上青天那般困难了。起码, 得进行一下记录, 可以这样: .“有个操作没成功, 不过对主流程没造成影响”, 等于真。