“我不知道这是否是出于无知,但我不喜欢这样
一种编程,因为它使用异常来执行流程控制。”
在Python世界中,使用异常进行流程控制是常见且正常的。
即使是 Python 核心开发人员也使用异常进行流程控制,并且这种风格已深深融入到该语言中(即迭代器协议使用停止迭代 http://docs.python.org/2.7/library/exceptions.html#exceptions.StopIteration发出循环终止信号)。
此外,try- except 风格用于防止某些程序中固有的竞争条件。“三思而后行” http://docs.python.org/2.7/glossary.html#term-lbyl结构体。例如,测试os.path.存在 http://docs.python.org/2.7/library/os.path.html#os.path.exists导致信息在您使用时可能已经过时。同样地,队列已满 http://docs.python.org/2.7/library/queue.html#Queue.Queue.full返回可能已过时的信息。这尝试除其他风格 http://docs.python.org/2.7/glossary.html#term-eafp在这些情况下将生成更可靠的代码。
“据我了解,异常不是错误,它们应该只是
用于特殊条件”
在其他一些语言中,该规则反映了其图书馆所反映的文化规范。该“规则”也部分基于这些语言的性能考虑。
Python 文化规范有些不同。在很多情况下,你must使用异常来控制流。此外,在 Python 中使用异常不会像在某些编译语言中那样减慢周围代码和调用代码的速度(即,CPython http://en.wikipedia.org/wiki/CPython已经实现了在每一步进行异常检查的代码,无论您是否实际使用异常)。
换句话说,你对“例外是为了例外”的理解是一条在其他一些语言中有意义的规则,但对 Python 来说则不然。
“但是,如果它包含在语言本身中,则必须有一个
这是一个很好的理由,不是吗?”
除了帮助避免竞争条件之外,异常对于将错误处理拉到循环之外也非常有用。对于解释型语言来说,这是必要的优化,因为解释型语言往往不会自动执行循环不变代码运动 http://en.wikipedia.org/wiki/Loop-invariant_code_motion.
此外,在处理问题的能力与问题发生的地方相去甚远的常见情况下,异常可以大大简化代码。例如,通常有顶级用户界面代码调用业务逻辑代码,而业务逻辑代码又调用低级例程。低级例程中出现的情况(例如数据库访问中唯一键的重复记录)只能在顶级代码中处理(例如要求用户提供不与现有键冲突的新键)。对这种控制流使用异常允许中级例程完全忽略该问题,并与流控制的该方面很好地解耦。
有一个关于例外的必要性的好博客文章 http://uberpython.wordpress.com/2012/09/23/why-im-not-leaving-python-for-go/.
另请参阅此堆栈溢出答案:异常真的是针对异常错误吗? https://stackoverflow.com/questions/180937/are-exceptions-really-for-exceptional-errors
“try- except-else 存在的原因是什么?”
else 子句本身很有趣。它在没有异常时但在finally 子句之前运行。这是它的主要目的。
如果没有 else 子句,在最终确定之前运行附加代码的唯一选择是将代码添加到 try 子句的笨拙做法。这很笨拙,因为它有风险
在不受 try 块保护的代码中引发异常。
在最终确定之前运行额外的不受保护的代码的用例并不经常出现。因此,不要期望在已发布的代码中看到很多示例。这有点罕见。
else 子句的另一个用例是执行在没有异常发生时必须发生的操作以及在处理异常时不会发生的操作。例如:
recip = float('Inf')
try:
recip = 1 / f(x)
except ZeroDivisionError:
logging.info('Infinite result')
else:
logging.info('Finite result')
另一个例子发生在单元测试运行程序中:
try:
tests_run += 1
run_testcase(case)
except Exception:
tests_failed += 1
logging.exception('Failing test case: %r', case)
print('F', end='')
else:
logging.info('Successful test case: %r', case)
print('.', end='')
最后,try 块中 else 子句最常见的用途是进行一些美化(在同一缩进级别上对齐异常结果和非异常结果)。这种使用始终是可选的,并不是绝对必要的。