Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发。

下文是Aaron Maxwell投递的客座博文,他是Advanced Python Newsletter的作者。

错误代码千千万,在Python中,有一种反面教材是难搞之王。

在其他两位工程师每人花费三天的时间试图去搞定一个Unicode编码的“玄学”问题而徒劳无功后,我仅仅花费了一天时间就定位到了错误的子句,尽管很累,但是很开心。十分钟后,我们就有了应对该bug的方法。

我们本可以用十分钟而不是宝贵的七天来解决这个问题,这样的事实让我们很痛苦。当然,这样说也有点鲁莽……

下面的这段代码就是关键点,这一小段代码是Python开发者能够写出来的最具有自我毁灭性的代码片段之一:

这一段代码还有很多其他的写法,如“except Exception:”或者“except Exception as e”,这给后续的工作带来了很大的麻烦:忽略和隐藏了错误的发生,并且不给出任何提示,否则在一般情况下类似的问题是很容易解决的。

为什么我说这段代码是当今Python世界中最可怕的代码呢?

l人们写这段话是因为知道这里发生某种特定类型的异常,然而,捕获异常后却忽略所有的错误……甚至是那些不可预料的异常。

l当这个bug出现时——经常出现,因为生产环境中总是有这样的代码——你可能都不知道代码库的哪部分出现了错误。这可能会耗费上好几小时沮丧的时光去才能发现错误竟然是出现在try语句块中。

l就算你发现了错误,在你想要解决问题时,却发现缺少必要的提示信息。这个错误/异常的类型是什么?涉及到哪些调用和数据接口?错误最开始出现在哪一个文件的哪一行代码?

l更糟糕的是,这很有可能伤害到在当前代码上工作的工程师的士气,乐趣甚至是自尊。错误出现时,故障排查人员可能需要花费几个小时去理解代码。他们会觉得自己是个糟糕的码农,因为他们需要几个小时才能找到错误。事实上并不是。捕获了异常而又对错误放任不管,这样的问题很难定位,排除,修复。

无论是独自工作还是作为团体中一份子,在我作为Python民工的十年开发经历中,这是我遇到的最能够打击士气,降低生产力和应用可靠性的代码片段,如果你有其他更厉害的代码,欢迎讨论。

我们为什么会写出这样的代码?

当然,没有人故意写这样的代码给团队成员增加压力和破坏应用的可靠性。我们之所以写这个是因为在try语句块中,代码在某些特定情况下可能会执行失败。乐观地进行尝试并且捕获异常是解决这种问题的一种很优秀,很Python的做法。

更阴险的是,去捕获异常,然后不报出任何对应的处理并不是这个可怕的想法中最糟糕的时候,然而,当你按下保存按钮时,你就将你的代码处于“万劫不复”的深渊:

lBugs能够在开发过程中避免被发现地命运,最终会被推送到实际生产环境中。

l当你发现bug的存在之前,它可能已经存活了数分钟,数小时,数天甚至是数周。

l这样的bug很难定位。

l即使你知道哪里会出现异常,你也很难去修复这个bug。

注意,我并不是说不去捕获异常。有很多必需的理由去捕获异常并进行处理,但就是千万不要让它静悄悄地溜走。比如当你处理一项至关重要的事务时,你甚至不想让它简单地执行完就算了,比较明智的做法是插入try语句来捕获异常,并把相应的堆栈追踪信息记录使用logging.ERROR记录下来,然后再继续执行。

解决之道

所以如果你不想捕获范围太宽广的异常,有什么替代的办法呢?有两种选择。

在大多数情况下,最好建议你去捕获更加特定的异常,如:

这是你首先应当做的尝试。它需要你对相关的代码有一些了解,如此才可能推断出会发生什么类型的异常。当你是第一次写自己的代码时,这种方法还是比较简单的。不过当清理别人代码时,这就会让你痛苦万分的。

如果有些代码需要捕获所有的异常,如在顶层循环中长时间运行的程序,捕获的每个异常需要把相关的堆栈追踪信息写入日志或者文件,同时要有相应的时间戳。如果你是使用Python的logging模块,做起来时非常简单的,每个logger对象都有叫exception的方法,它接受一个字符串做参数。如果你在异常捕获的时候调用这个方法,捕获的异常连同堆栈追踪信息都会被自动记录下来。

这个日志包含错误信息,后面几行是堆栈追踪信息。

歪瑞一贼!

如果你的应用程序并不是使用logging模块来进行记录呢?假设你不想重构你的代码,你仅仅需要找到异常语句,并对堆栈追踪信息进行格式化输出。在Python3里很容易做到的。

在Python2中,你再多做一丢丢工作就好了:因为exception对象没有相对应的堆栈追踪信息。你可以在except语句块中调用sys.exc_info()函数来实现。

正如你所看到的,你可以把上述两种代码中的traceback-logging函数进行整合,从而可以忽略你是在Python2还是Python3下工作。

挽救措施

“好的吧,Aaron,你成功说服了我。我为我过去做的蠢事而流泪悔恨。我现在能做点什么补救措施?”我很高兴你这样问,下面的一些方法你可以尝试一下。

在你的编程规范中明确地制止它

如果你的团队有代码评审这一环节,你们应该会有代码编写的指导手册。如果没有,也很容易创建——就跟新建一个wiki页面一样。你需要把以下两条建议加入:

l如果有些代码需要捕获所有的异常,如在顶层中长时间运行的程序语句,那么每个捕获的异常都需要将其相关的堆栈追踪信息记录下来,包括时间戳。不仅仅是异常的类型和信息,也包括整个的追踪信息。

l对于其他的大多数异常来说,尽可能捕获精确类型的异常,如值错误,连接超时等。

为已经存在的except字句列清单

上面的方法能够帮助你避免未来的错误。然而已经存在的异常捕捉过广的except语句怎么办?很简单:在bug追踪系统中列出except字句清单,然后去一个个地修复它们。这是简单且有效的解决问题的办法。你可以现在就着手去做。

我建议你为每一个仓库或应用去建立清单,在你的代码中找到每个Exception,然后去优化它(你只需要在代码库通过检索找出“except”和“except Exception”)。你可以把它转换成处理某种特定类型的异常,或者如果你对代码不清楚的话,修改except语句去记录堆栈追踪信息。

你还可以进一步优化,为需要指定特定类型的异常建立一个清单。如果感到这个异常可以更加精确时,但是你对代码的内部结构不清楚时可以这样做。在这种情况下,你需要记录该异常的堆栈追踪信息;单独为此创建一个清单来记录;把它委派到某个对代码更加了解的家伙手里。如果你在单个的try/except中花费超过5分钟的时间去思考找出一个特定的异常时,我推荐你这样做。

培训团队中的其他成员

你是否参加定期的工程会议?一周一次?两周一次?还是一个月一次?你可以讲解这个“反面典型”,讲一下它对团队生产力造成的危害和及其简单的解决办法。

更好的是,你可以直接去找技术负责人或者工程经理来阐述这个问题。因为他们也对团队的生产力很关心。你可以把这篇文章的链接发给他们。如果有需要的话,让我跟他们直接通话来说服他们。

你甚至可以扩展到更广的社区。你是否会参加Python相关的工程师聚会?他们是否有“闪电会谈”这种环节?你是否可以在下次聚会时申请5到15分钟的发言时间?把这个伟大的“福音”传递给他们。

为什么要记录所有的堆栈追踪信息?

在上面的文字中,我一次次说到记录所有的堆栈追踪信息,而不仅仅是异常的信息。这样看起来可能工作量更大一点,你可能会迷失一个个与之相关的模块中。难道仅仅记录信息本身不就够了么?

不,远远不够。即使是一个经过精心设计的异常信息也只能告诉你异常语句在哪里,在哪个文件的哪一行里。通常来说并没有缩减多少范围。好吧,我们来假设一下最好的情况,当然,仅仅记录信息也比什么都不记录好,但是它并不会告诉你问题的源头在哪。很有可能出现在一个完全不同的文件或者模块里面,而人工去猜测很难办到的。

更复杂的是,在真实的开发环境中,团队成员的各种代码都有可能调用抛出异常的语句。也许当Foo类中的bar方法调用时会出现,但函数bar()调用时却不会出现异常。仅仅记录错误信息是无法区分这两者的区别的。

最近我所经历的事故发生在一个中等规模的开发团队中,大概50人,我是新来的,负责处理近四个月来一直出现的Unicode编码问题。异常被捕获了,消息被记录了,但是没有其他的信息可供参考。两个资深的工程师已经在这个问题上花费了数日时间,最终放弃了,他们没法找到问题所在。

他们是德高望重的程序员,最终,出于无奈,他们把这个问题交给了我,借助他们先前的经验,我成功复现了问题的发生,并记录下相关的堆栈追踪信息。六个小时之后,我找到了问题所在。一旦我设置了堆栈追踪信息,你知道我用多长时间就解决这个问题了么?

十分钟。仅仅十分钟。一旦我们有了堆栈追踪信息,修复的方法自然就有了。如果能够提早记录堆栈追踪信息,我们本可以节省下一个工程师一周的时间。还记得我前面说过的话么?在你解决问题时,设置堆栈追踪信息与否,解决bug的时间可能是几天或者是几分钟。我可不是开玩笑的。

(有趣的是,这样的经历也有了一个好的结果,它促使我开始书写更多关于Python的内容,我们能够更加高效地利用这门语言。)

英文原文:https://realpython.com/blog/python/the-most-diabolical-python-antipattern/

译者:崔子橙

python.freelycode.com-最难搞的python“反面”代码相关推荐

  1. 学python和java哪个难?,java和python哪个难学

    java和python哪个好学 ①python比Java简单,学习成本低,开发效率高;②Java运行效率高于python,尤其是纯python开发的程序,效率极低;③Java相关资料多,尤其是中文资料 ...

  2. python flask和django_真正搞明白Python中Django和Flask框架的区别

    在谈Python中Django框架和Flask框架的区别之前,我们需要先探讨如下几个问题. 一.为什么要使用框架? 为了更好地阐述这个问题,我们把开发一个应用的过程进行类比,往往开发一个应用(web应 ...

  3. python分布爬虫_13天搞定Python分布爬虫(第七天)(Scrapy)

    43-爬虫的基本使用 pip install Scrapy 注:windows平台需要依赖pywin32,pip install pypiwin32 创建工程:开发工具命令行(Terminal):sc ...

  4. python基础知识-一篇文章搞定Python全部基础知识

    原标题:一篇文章搞定Python全部基础知识 前言: 1.Python软件安装 第一章.字符串及数字变量 1.变量 要点提炼:Python变量为强类型动态类型.换言之,变量很任性,你给他int,他就是 ...

  5. python数据分析建模-十分钟搞懂“Python数据分析”

    原标题:十分钟搞懂"Python数据分析" 引言:本文重点是用十分钟的时间帮读者建立Python数据分析的逻辑框架.其次,讲解"如何通过Python 函数或代码和统计学知 ...

  6. python爬虫分布图_13天搞定Python分布爬虫!成为炙手可热的爬虫工程师

    原标题:13天搞定Python分布爬虫!成为炙手可热的爬虫工程师 1.什么是爬虫? 网络爬虫也叫网络蜘蛛,如果把互联网比喻成一个蜘蛛网,那么爬虫就是在网上爬来爬去的蜘蛛,爬虫程序通过请求url地址,根 ...

  7. python修饰器太难搞_【Python】小说爬虫界面版(各种BUG已修复)

    [Python] 纯文本查看 复制代码import tkinter as tk import re import urllib.request import os import time from u ...

  8. python import from class_彻底搞懂Python 中的 import 与 from import

    对不少 Python 初学者来说,Python 导入其他模块的方式让他们很难理解.什么时候用import xxx?什么时候用from xxx import yyy?什么时候用from xxx.yyy ...

  9. 百道Python面试题实现,搞定Python编程就靠它

    机器之心报道 机器之心编辑部 想要备战 Python 面试,这两个项目有千道 Python 问题与实现. 之前机器之心介绍了 PHD 大牛的求职之路,很多读者感觉这位大牛太厉害了,他的经历对我们帮助不 ...

最新文章

  1. visio 2010 修改 默认字体 字号大小 方法
  2. 机器学习知识点(九)BP神经网络Java实现
  3. js react根据几个颜色点计算区间任意数值对应颜色
  4. 编程关键词介绍...
  5. 在Windows 下使用CodeBlocks 自带编译器实现对编译的优化
  6. 在汇编语言调用c语言程序,C/C++调用汇编语言实例:目录表程序
  7. phaser H5游戏框架的学习相关推荐教程
  8. 各种动漫情侣姿势的画法
  9. 解决flex布局的space-evenly兼容性问题
  10. 商务网站建设与维护【9】
  11. 软考-软件设计师 UML建模
  12. 苹果 Apple Beta 版软件计划 相关软件地址
  13. Vue3+Vite+TS后台项目 ~ 10.商品管理
  14. 互联网电影院带来新突破,5G+4K’
  15. 如何清除DNS缓存(Mac,Windows,Chrome)
  16. 计算机科学计算的方面,计算机的科学计算功能在工程领域中的应用.doc
  17. 使用Javascript无限添加QQ好友原理解析
  18. webpack打包压缩混淆_前端打包利器:webpack工具
  19. npm 和 cnpm 的区别
  20. 系统架构师(软考)------网络互联模型与常见的网络协议

热门文章

  1. go语言学习(7)锁
  2. Alluxio : 开源分布式内存文件系统
  3. 给你一个网站你是如何来渗透测试的
  4. 信令风暴研究现状总结
  5. IPK僵尸网络 看看其传播手法
  6. LSTM入门学习——结合《LSTM模型》文章看
  7. FATE 集群部署 step2
  8. pymysql Key error:255的解决办法
  9. 美化浏览器的radio和checkbox样式
  10. 解决导入第三方图片JS出现403问题