0%

如何写出整洁的Python代码 —— 异常处理 (译)

本文用于练习英文阅读,如有侵权,联系删除

原文链接Jeff Knupp写的Write Cleaner Python: Use Exceptions

以下为译文
许多程序员深受无论在任何语言中,只有真正特殊的异常情况才可以使用异常。他们错了,Python社区建议使用异常处理让你的代码更整洁干净。而且这通常不会像其他语言那样对性能造成巨大影响。

使用异常编写更整洁的代码

当我谈到“使用异常”是 我不是指那些疯狂的在尽可能出问题的地方捕获异常的层次结构。毫无疑问,这种做法会导致代码难以阅读和维护。这个概念已经被广泛的讨论,并在Joel Spolsky’s 的Blog里得到很好的总结。

注:Python 避免了大多数使用错误码还是使用异常的矛盾情况。在一个函数中返回多个值还是返回一个不同类型的值(例如None 或类似的)的讨论不是本文的重点。

我提倡的是一种完全不同的异常用法,简言之:着重使用已经抛出异常的Python 内置模块和标准库 异常机制已经成为了Python 底层构建。事实上,我保证你的代码已经使用了异常处理,即使您没有显式的使用。

插曲: for 语句是如何工作

每当你使用for 去遍历一个可迭代对象(基本上,所有序列类型 都定义了 iter() 或__getitem__() 以支持遍历)时, 它需要知道合适停止迭代。看下面的代码:

1
2
3
words = ['exceptions','are','useful']
for word in words:
print(word)

for 如何知道当到达到最后一个元素时应该停止尝试继续遍历?答案可能让你惊讶:这个列表抛出了一个StopIteration 异常。

事实上,所有可迭代对象都遵循这种模式。 当for第一次评估一条语句时,它会调用iter(),这会生成一个可迭代对象:能按顺序返回该对象的内容。为了被iter()成功调用,这些对象必须支持可迭代协议(通过定义 iter() 或序列协议(定义 getitem())。

碰巧的是 当被迭代的元素用尽时 iter() 和 getitem()都需要引发异常。 iter() 抛出 StopIteration 异常, getitem() 抛出 IndexError 异常。 这就是for 如何知道该停止迭代的方法。

总结: 如果你在代码的任何地方使用过for,那么你正在使用异常


LBYL(提前检查许可) vs EAFP(求原谅比求许可更容易)

在核心Python构造中广泛的使用异常非常好,但这为什么要这么做。毕竟,for 完全可以写成不依赖任何异常作为序列结束标记的方式。确实,完全可以避免异常。

但是他们的存在是由于Python的的错误检查哲学。不使用异常的代码将始终检查是否可以执行某些操作。事实上,在确信可以做某事之前,必须做很多判断。如果他没有 考虑全面,就会发生错误,思考以下代码:

1
2
3
4
5
6
7
8
9
10
11
def print_object(some_object):
# check if the object is printable...
if isinstance(some_object,str):
print(some_object)
elif isinstance(some_object,dict):
print(some_object)
elif isinstance(some_object, list):
print(some_object)
# 97 elifs later...
else:
print( "unprintable oject")

这个简单的函数负责调用print()打印一个对象,如果对象类型不支持打印就显示一个错误。

视图预测所有的错误情况注定会导致失败(并且非常的丑陋)。鸭子类型(当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子)是Python的中心思想,这个函数可以不用明确的检查类型,依然正确打印错误,像这样重写:

1
2
3
4
5
6
7
def print_object(some_object):
# Check if the object is printable
try:
printable = str(some_object)
print(printable)
except TypeError:
print('unprintable ojbect')

如果可以将对象强制转换为字符串,就打印它,如果引发异常,则打印错误消息。相同的想法,但是更容易理解(在try 里面显然可以合并为一行,但是并不能使示例更清晰可读)。另外,注意 我们正在显式的检查 TypeError,如果强制转换失败,仍然会抛出异常,永远不要使用只使用一个 except 这将会隐藏你不想捕获的错误(导致代码难以维护)。

参考

欢迎关注我的其它发布渠道