在 Python 中,我不时会看到以下内容:
try: try_this(whatever) except SomeException as exception: #Handle exception else: return something
try-except-else 存在的原因是什么?
我不喜欢那种编程,因为它使用异常来执行流控制。但是,如果它包含在语言中,那肯定是有充分理由的,不是吗?
我的理解是异常不是错误 ,它们应该只用于异常情况(例如我尝试将文件写入磁盘并且没有更多空间,或者我没有权限),而不是用于流控制。
通常我将异常处理为:
something = some_default_value try: something = try_this(whatever) except SomeException as exception: #Handle exception finally: return something
或者如果发生异常我真的不想返回任何东西,那么:
try: something = try_this(whatever) return something except SomeException as exception: #Handle exception
“我不知道是不是因为无知,但我不喜欢那种编程,因为它使用异常来进行流控制。”
在 Python 世界中,使用异常进行流控制是常见且正常的。
甚至 Python 核心开发人员也使用异常来进行流控制,并且这种风格在语言中很重要(即迭代器协议使用 StopIteration 来表示循环终止)。
此外,try-except 样式用于防止某些“look-before-you- leap”结构中固有的竞争条件。例如,测试 os.path.exists 会导致信息在您使用时可能已经过时。同样, Queue.full 返回可能是陈旧的信息。在这些情况下,try-except-else 样式将生成更可靠的代码。
“我的理解是异常不是错误,它们应该只用于异常情况”
在其他一些语言中,该规则反映了他们的文化规范,正如他们的图书馆所反映的那样。“规则”也部分基于这些语言的性能考虑。
Python 文化规范有些不同。在许多情况下,您 必须 对控制流使用异常。此外,在 Python 中使用异常不会像在某些编译语言中那样减慢周围代码和调用代码的速度(即CPython已经在每一步都实现了用于异常检查的代码,无论您是否实际使用异常)。
换句话说,您对“例外是为例外”的理解是在其他一些语言中有意义的规则,但对于 Python 则不然。
“但是,如果它包含在语言本身中,那肯定是有充分理由的,不是吗?”
除了有助于避免竞争条件外,异常对于将错误处理拉到循环外也非常有用。这是解释语言中的必要优化,这些语言不倾向于具有自动循环不变的代码运动。
此外,在处理问题的能力与问题出现的地方相去甚远的常见情况下,异常可以大大简化代码。例如,通常有顶级用户界面代码调用业务逻辑代码,而这些代码又调用低级例程。低级例程中出现的情况(例如数据库访问中唯一键的重复记录)只能在顶级代码中处理(例如要求用户提供与现有键不冲突的新键)。对这种控制流使用异常允许中级例程完全忽略该问题,并与流控制的这方面很好地分离。
这里有一篇很好的关于异常的必要性的博客文章。
另外,请参阅 StackOverflow 答案:异常真的是异常错误吗?
“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 子句用于美化(将异常结果和非异常结果对齐在同一缩进级别)。这种使用始终是可选的,并不是绝对必要的。