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

age = numpy.array([10.0, 20.0, 30.0, 40.0, 50.0])

height = numpy.array([60.0, 66.0, 72.0, 63.0, 66.0])

接下来证明它确实会将我们给的范围转化到[0 - 1]区间内:

normalize_ranges(‘age’)

{‘max’: 1.0, ‘min’: 0.0}

好了,这是一个很短的测试,但是它似乎起作用了。我们通过一系列的“真实”的数字和把它规范到区间(0 - 1)内。

记住上述指导方针,让我们找到这个函数中存在的错误。代码中隐含的假设是什么?

我能看到一些假设:

  1. original_range只包含正数

  2. 返回的比率在0.0和1.0之间

经过仔细检查,上面的代码中存在相当多的假设。不幸的是,浏览代码时它们不会立刻明显地表现出来。要是我们能使这些假设更明晰……

声明语句

声明语句在单元测试中相当常见。事实上,Python为单元测试定制了大量的声明语句。然而,没有任何理由简单地将这个有用的工具用在测试环境中。

常规代码里的声明语句非常有用。这些声明语句需要一个表达式,如果表达式为false,则会抛出一个Asserti异常以及一个可选消息。

例如,我们上面的函数总是返回一个在(0 - 1)之间的值。不幸的是,我们的假设更多地显示程序并没有达到预期:

你可以想象,这种情况很容易被忽视很长一段时间,这个返回值会在整个代码中传播。这正是本文开头的故事:错误的准确类型不可能被发现而导致的可悲故事。

我们可以试着考虑到用户可能输入的每个值并妥善处理它。事实上,这是正确的做法,但不能保证我们不会遗漏数据。我们已经承认了程序员会犯错这一事实。

幸运的是,既然我们已经承认我们犯错误,我们可以使用声明语句在以后重构代码。

我们添加了一些声明语句,如果返回值不在预期范围之内,它们会发出警告。让我们来看看这些声明如何改变我们的测试代码:

这种方法有以下几个优点:

  • 它是一种可执行文档
  • 在问题的根源设置警告器
  • 包含有价值的关于“无效”参数的调试信息

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消息框形式弹出

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

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

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

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值