在Python中,我不时看到以下块:
try:
try_this(whatever)
except SomeException as exception:
#Handle exception
else:
return something
除了其他原因,尝试的原因是什么?
我不喜欢这种编程,因为它使用异常来执行流控制。然而,如果它被包含在语言中,肯定有一个很好的理由,不是吗
我的理解是,异常不是错误,它们只能用于异常情况(例如,我试图将文件写入磁盘,但没有更多空间,或者可能我没有权限),而不能用于流控制
通常情况下,我将异常处理为:
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 style将生成更可靠的代码
在其他一些语言中,这一规则反映了他们的图书馆中反映的文化规范。“规则”还部分基于这些语言的性能考虑
Python文化规范有些不同。在许多情况下,您必须对控制流使用异常。此外,在Python中使用异常不会像在某些编译语言中那样减慢周围代码和调用代码的速度(即CPython已经在每个步骤中实现了异常检查代码,而不管您是否实际使用异常)
换句话说,您对“异常是针对异常的”的理解在其他一些语言中是有意义的,但在Python中是没有意义的
除了有助于避免竞争条件外,异常对于将错误处理拉到循环之外也非常有用。在解释语言中,这是一个必要的优化,因为解释语言往往没有自动loop invariant code motion
此外,在处理问题的能力与出现问题的地方相去甚远的常见情况下,异常可以大大简化代码。例如,通常有顶级用户界面代码调用业务逻辑的代码,而业务逻辑又调用低级例程。低级例程中出现的情况(如数据库访问中唯一键的重复记录)只能在顶级代码中处理(如要求用户提供与现有键不冲突的新键)。对此类控制流使用异常允许中级例程完全忽略该问题,并与流控制的这一方面很好地解耦
有一个nice blog post on the indispensibility of exceptions here
另外,请参见堆栈溢出回答:Are exceptions really for exceptional errors?
else子句本身很有趣。它在没有异常但在finally子句之前运行。这是其主要目的
如果没有else子句,在完成之前运行其他代码的唯一选项是将代码添加到try子句的笨拙做法。这是笨拙的,因为它有风险 引发代码中不受try块保护的异常
在完成之前运行额外的无保护代码的用例并不经常出现。因此,不要期望在已发布的代码中看到许多示例。这有点罕见
else子句的另一个用例是执行在没有异常发生时必须发生的操作,以及在处理异常时不发生的操作。例如:
另一个例子出现在unittest Runner中:
最后,try块中else子句最常见的用法是为了美化(将异常结果和非异常结果在相同的缩进级别上对齐)。这种使用始终是可选的,并非绝对必要
Python不同意异常只应用于异常情况的想法,事实上,习惯用法是'ask for forgiveness, not permission'。这意味着将异常作为流程控制的常规部分是完全可以接受的,而且事实上是受鼓励的
这通常是一件好事,因为以这种方式工作有助于避免一些问题(作为一个明显的例子,通常可以避免竞争条件),而且它往往会使代码更具可读性
假设您有一种情况,您接受一些需要处理的用户输入,但有一个已处理的默认值。
try: ... except: ... else: ...
结构使代码非常可读:与它在其他语言中的工作方式相比:
注意优点。不需要检查值是否有效并单独解析它,它们只执行一次。代码也遵循一个更符合逻辑的进程,主代码路径是第一个,然后是“如果不起作用,就这样做”
这个例子自然有点做作,但它显示了这种结构的一些情况
try
块允许您处理预期的错误。except
块应该只捕获准备处理的异常。如果处理意外错误,代码可能会出错并隐藏bug如果没有错误,将执行
else
子句,并且通过不在try
块中执行该代码,可以避免捕获意外错误。同样,捕获意外错误可以隐藏bug示例
例如:
“try,except”套件有两个可选子句,
else
和finally
。所以它实际上是try-except-else-finally
else
仅当try
块中没有异常时才会求值。它允许我们简化以下更复杂的代码:因此,如果我们将
else
与备选方案(可能会产生bug)进行比较,我们会发现它减少了代码行,并且我们可以拥有一个更可读、可维护和更少bug的代码库finally
finally
将在任何情况下执行,即使使用return语句对另一行进行求值用伪代码分解
它可能有助于将其分解,以尽可能小的形式展示所有特性,并添加注释。假设函数中存在语法正确(但除非定义了名称,否则无法运行)的伪代码
例如:
诚然,我们可以将
else
块中的代码包含在try
块中,如果没有异常,它将在该块中运行,但是如果该代码本身引发了我们正在捕获的那种异常呢?将其保留在try
块中会隐藏该bug我们希望最小化
try
块中的代码行,以避免捕获我们没有预料到的异常,其原则是,如果我们的代码失败,我们希望它失败。这是一个best practice在Python中,大多数异常都是错误
我们可以使用pydoc查看异常层次结构。例如,在Python 2中:
或Python 3:
会给我们等级制度。我们可以看到大多数类型的
Exception
都是错误,尽管Python将其中一些错误用于结束for
循环(StopIteration
)。这是Python 3的层次结构:一位评论人士问道:
不,您不返回异常,只需使用裸
raise
重新调用它以保留堆栈跟踪或者,在Python 3中,您可以引发新异常,并使用异常链接保留回溯:
我在my answer here中详细阐述
相关问题 更多 >
编程相关推荐