你所不知道的,Python中的防御性编程_python防御性编程有哪些技术(1)

本文探讨了可执行文档如声明语句和doctest在设置警告器和提供关于无效参数的调试信息方面的价值。同时,作者强调了日志记录的灵活性和单元测试作为防止bug重现的重要性,以及如何平衡使用这些工具以提高代码质量和可维护性。
摘要由CSDN通过智能技术生成
  • 它是一种可执行文档
  • 在问题的根源设置警告器
  • 包含有价值的关于“无效”参数的调试信息

1. 作为一种可执行文档

典型文档通常有几种不同的特点如内联或块注释,文档字符串,以及sphinx。这些特点具有特定目的,它们几乎都是软件开发的关键。不幸的是,他们都遇到了同样的问题。它们可以很快地与快速变化的代码和需求保持同步。这会导致开发人员不信任原始文档。

将声明文档化有不同的目的。他们清晰而简明地描述应用程序在运行时的预期状态。此外,如果我们改变我们的假设时没有修改声明以匹配新的行为,应用程序就会崩溃。

声明语句最好随其他变动一起更新。因此声明比非可执行文档更值得信赖。此外,断言还具有许多诸如评论,文档字符串等的益处。

值得指出的是,有另一种被称为doctest形式的可执行文档,在Python文件系统中十分常见。这些测试/文档可能有些丑陋,但他们的关键特性是他们近乎于代码,就像声明一样。

2. 在问题的根源设置警告器

我们都有过类似的经历,你花费几个小时调试bug,最后意识到真正的bug一开始就不在开始调试的代码中(见5个为什么)。也许导致bug的原因根本就不是你第一次注意到的错误行为。

例如,你在系统中发现一个字节字符串,但你认为内部都是Unicode字符串。可能需要很长时间才能找到格式转换功能在哪里失效了。这处境真是令人沮丧。如果更早发现了bug就好了,或者至少有更多的调试信息。

声明语句不会阻值这种情况的出现,但它们确实提供了一个改善的机会。上面的声明将警告我们:这时该函数不服从它们之间的约定,没有返回一个(0 - 1)区间内的值。如果我们随后发现其他代码的范围无效,它会给我们一个有价值的线索。我们将会知道这个函数没有按照既定要求执行。这一条线索可以节省时间,因为它可以避免从症状的根源追溯其发生原因。

3. 包含有价值的关于“无效”参数的调试信息

请注意到我们声明语句还包括输入参数的信息。当用户使用那些我们没有访问权的数据而遇到错误时,这些信息将具有无可估量的价值。此外,当用户很难讲清楚错误发生时的情况时,调试信息尤其有用。因此,这些声明语句可以防止你无意把bug标记为“不可重现”状态。

输入参数信息也有其他一些微妙的好处:

  • 显示一条关于用户正在使用何种类型数据的无效假设
  • 解释我们在文档中期望使用何种类型数据的疏忽
  • 使潜在的无法执行的新实例暴露出来

使用声明语句的坏处

我们已经编写好可以提供大量好处的声明语句,但有时候它们表现得并不有效。通常,有以下几个缺点:

1. 调试模式

通常情况下,出于对技术和显示情况的考虑,声明语句并不符合代码编写规范。它们仅仅在调试常量为true时激活。然而,常量默认为true,这意味着你的代码很有可能在发布时处于调试模式。

如果你的应用程序运行在少量额外逻辑频繁引起注意的环境中,就需要注意一下了。关闭调试模式的唯一方法是以-O 选项运行Python解释器。

2. 增加代码的噪声

很容易过度使用声明语句而使代码很快变得难以阅读。这会使你的代码混入很多噪声,而实际功能被一系列的错误检查淹没。下面的代码是一个过度使用声明语句的示例,让我们看看想明白代码到底干什么有多困难。

适当地使用声明

假设你认定某些错误绝不会发生,就该保守地使用声明语句。不要过分使用声明语句去检查无效输入。

这没有硬性规定,每个开发人员可能会不同程度地使用它。请尝试采用你自己的标准,包括一些在你的开发中定义的风格指南。

你应该有自己的风格吧?

当然,也要记得Python支持duck-type类型(译注:鸭子类型,动态类型的一种),因此不要因为过度使用是明语句检查数据类型而把这种功能淹没了。

我发现的一个有用的技术是将所有捕捉到的Asserti异常优先级设置为最高,并结合另一个有用的技术进行处理。

日志记录

日志的使用类似于声明语句。它可以提供运行时调试信息以改进你的可执行文件。然而,日志并不完全像声明。它有一些额外的好处。

1. 更出色的控制粒度

Python的日志模块包含面非常广泛,并且可定制。你可以把消息发送到几个不同的级层,每一层可以开启或关闭。因此,你最好考虑一些更为严重的情况,以帮助你在这个日志级层编码。

记住,这里的情况与声明语句无关,它们仅仅依赖于调试模式。

2. 更好的动态控制

日志记录允许你随地浏览日志级别。常见情况是一个配置文件,环境变量或是数据库。这种灵活性允许你控制日志信息显示量,而不必重新运行或重新部署应用程序。

相比之下,Python不允许为声明语句动态分配调试常量。因此,你无法在不重新运行应用程序的前提下打开声明开关。

决定你的防御编码策略时,值得考虑一下它。如果你使用了一个“可代替”的工具,动态日志控制就显得尤其重要,如PyInstaller分配机制。

3. 静默保存回溯信息

发生错误时实时保存回溯和调试信息相当重要,智能日志可以自动完成保存过程。

这个概念是道格·赫尔曼在一个著名的的异常处理程序中提出的:

自动调用 log.exception 会带给我们更多异常消息。然后,我们可以配置日志记录器将回溯和异常消息分别存储到不同日志文件,方便以后在没有正常消息和警告的情况下检查程序。

如果在运行代码中启用这个异常文件,它可能包含非常有用的调试信息。这为分析日志数据创造了很多令人兴奋的可能性:

  • 用户会尝试不同排列的特性,而这些特性我们从来没有测试甚至没有考虑过,这可能导致新特性变多,以使常见实例更加易用。
  • 找到由于用户误解应用程序的工作方式而犯的类似错误,这可能为编制更好的用户文档作出相当贡献。

4. 声明的高级组合

你还可以使用声明语句配合日志记录。例如,你可以在默认调试模式下运行应用程序,然后捕捉Asserti并将日志记录到另一个文件。这可能为数据挖掘创造更多的可能性,如发现一个假定在平台中运行的环境。

这只是几个使用日志的防御性编程的例子。事实上,你可以使用日志创建低保真环境以解决各种各样的问题,另一篇文章有相关介绍。[2]

日志记录的缺点

日志和声明有许多相同的缺点。然而,日志的额外的灵活性伴随着额外的负担需要考虑。

1. 难度管理级别一致性

使用日志记录最大的困难是相同级别的一组日志始终贯穿整个代码库。这可以归为主观的命名问题,这两个问题仅在计算机科学中存在。最好的解决方案是随代码风格一同提交指南。随后添加日志消息时,会有一些具体项目的文档供新手参考。

2. 设置多个日志记录器

日志模块是非常灵活的,但它是有代价的。日志配置将变得很复杂。考虑采用以下策略:

  • 调试级别的消息保存在一个名为.debug的隐藏文件中
  • 信息、警告和错误级别消息保存在stderr文件中
  • 关键和异常级别的消息以GUI消息框形式弹出

这是一个很好的起点。同时,Python的文档包括几个绝对值得一读的优秀日志记录策略,决定你的设置之前,最好读一下。

记住,当你调试bug时,日志可以表现出相当大的优势。因此,一定要花时间去学习日志系统,确保您的配置设计得足够仔细。即使简单的应用程序也应该有一套良好的、精心设计的日志记录策略。

单元测试

保护自己免遭bug烦扰的最后一种方法是不要忽略过去的bug。过去的bug倾向于长时间潜伏在代码中,这通常是由于有人不理解为什么一些代码要以一种特殊的方式编写,继而删除了它们以”清洁“系统。

这就引出了另一个场景,创建另一种版本的可执行文件相当有用。因此,当一个毫无戒心的开发人员修改一些代码或者删除一些正在运行的代码来警告他们的错误,就没有什么灾难性后果了。

人们通常在测试驱动开发的背景下会遇到单元测试的情况。这是一个很好的概念,但往往在实践中有点过于乐观因而很难遵循(阅读材料: customer wants code now)。相关的讨论在另一个博客帖子中。

我想讨论的是如何使用单元测试使你从今后和过去的bug中解脱出来。我认为它将TDD测试和一些更有成效的能够减少前期时间成本的概念颠倒了。

我建议你修复bug后写一份测试报告 (更多讨论)

这里有几个可能不会立即表现出来的目的:

改善bug修复的文档化

要确保你提交的信息包含帮助你修复bug的内容,但不要就此止步。很可能你提交的信息和评论缺乏类似于针对以下提问的解决方案:

  • 你如何测试补丁?
  • 具体是哪种情况导致了这个bug?

这就是一个优秀的单元测试开始起作用的时候了。你已经知道如何测试bug。(你之前做过测试,对吧?)所以,描述你测试的场景吧,让每个人都受益于你的努力。

单元测试是一个出色的环节,在这里所有的bug将展现出来,并且它会提供修复bug的文档。你不仅可以解释如何以及为什么你要修复它,而且你测试它的方式也能细细道来。如果bug又出现了,那么这条信息将会非常有价值。

不要忘记,一次正常运行的测试可以为bug的定位线索(让你知道bug没有在这里发生)。

2. 能经得住时间考验的重复bug排除编码

特定bug不知不觉地嵌入代码库并不是稀罕事。由于需求变化,重构错误,或其他部分的变动,这是可能发生的。可以通过为一个特定的bug修复编写一个单元测试回归,且要记得运行你的测试代码。将单元测试看做是从稍后又会遭遇bug的麻烦中解放出来的机会。

单元测试的负面影响

单元测试的主要缺点是,你很容易忘记运行它们。这只是一个无法从代码定位的测试副产品。通过类似的实践,像持续集成化使用Travis CI 和自动化测试,这个不足可以得到弥补。

另一个缺点是单元测试通常只在自己的测试环境中执行,这是一个没有真实用户的模拟环境。

结论

这种风格的发展很难分类,不幸的是没有任何既定的规则说明何时使用它。所以,我强烈建议你牢牢记住指南。

该指南将导致你的观念发生微妙的变化。观念的变化相当重要,而不是工具和机制本身。最终你会因为过度使用声明或日志记录犯一些错误,进而开始形成自己的风格。同时,每一个项目的需求都不同,所以有必要学会所有的工具,进而以需要的解决方式混合使用它们。

脚注

[1]可执行文件是一个术语,有时用来描述测试框架doctests。术语“Literate testing”用来描述这一概念。

[2]你甚至可以使用日志来构建自己的分析工具。登录到网络或Dropbox文件,每次将使用一个特性。然后,后台将有一个shell脚本用来收集这些文件。现在你有基于简单文本格式的大量使用信息,它包含开放的大量数据分析的可能性,你可以用来帮助你的用户。

文末有福利领取哦~

👉一、Python所有方向的学习路线

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。img

👉二、Python必备开发工具

img
👉三、Python视频合集

观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。
img

👉 四、实战案例

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。(文末领读者福利)
img

👉五、Python练习题

检查学习结果。
img

👉六、面试资料

我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。
img

img

👉因篇幅有限,仅展示部分资料,这份完整版的Python全套学习资料已经上传

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里无偿获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

👉因篇幅有限,仅展示部分资料,这份完整版的Python全套学习资料已经上传

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里无偿获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值