在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
我想说,仅仅因为没有人发表过这种观点
避免在try/except中使用else子句,因为大多数人都不熟悉它们
与关键字try、except和finally不同,else子句的含义不是不言而喻的;可读性较差。因为它不经常使用,它会导致阅读代码的人想要再次检查文档,以确保他们理解了发生了什么。
(我写这个答案正是因为我在我的代码库中发现了一个try/except/else,它引起了一个wtf时刻,迫使我做一些谷歌搜索)。
因此,无论我在哪里看到类似OP示例的代码:
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
else:
# do some more processing in non-exception case
return something
我更倾向于重构
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
return # <1>
# do some more processing in non-exception case <2>
return something
<1>显式返回,清楚地表明,在异常情况下,我们已经完成了工作
<2>作为一个不错的小副作用,原来在else块中的代码被降低了一级。
这是我关于如何理解Python中的try-except-else-finally块的简单代码片段:
def div(a, b):
try:
a/b
except ZeroDivisionError:
print("Zero Division Error detected")
else:
print("No Zero Division Error")
finally:
print("Finally the division of %d/%d is done" % (a, b))
我们试试div 1/1:
div(1, 1)
No Zero Division Error
Finally the division of 1/1 is done
我们试试div 1/0
div(1, 0)
Zero Division Error detected
Finally the division of 1/0 is done
Python不赞同异常只用于异常情况的想法,事实上,习惯用法是“请求原谅,而不是许可”。这意味着使用异常作为流控制的常规部分是完全可以接受的,实际上是被鼓励的。
这通常是一件好事,因为以这种方式工作有助于避免一些问题(一个明显的例子是,经常避免竞争条件),而且它往往使代码更具可读性。
假设您有这样一种情况,需要处理一些用户输入,但默认值已经处理完毕。尝试:……除了:…其他:…结构使得代码可读性非常强:
try:
raw_value = int(input())
except ValueError:
value = some_processed_value
else: # no error occured
value = process_value(raw_value)
与它在其他语言中的工作方式进行比较:
raw_value = input()
if valid_number(raw_value):
value = process_value(int(raw_value))
else:
value = some_processed_value
注意它的优点。没有必要检查值是否有效并分别解析它,它们只执行一次。代码也遵循一个更有逻辑的顺序,主代码路径是第一个,然后是“如果它不起作用,就这样做”。
这个例子自然有点做作,但它显示了这种结构的一些情况。
我想说,仅仅因为没有人发表过这种观点
避免在try/except中使用else子句,因为大多数人都不熟悉它们
与关键字try、except和finally不同,else子句的含义不是不言而喻的;可读性较差。因为它不经常使用,它会导致阅读代码的人想要再次检查文档,以确保他们理解了发生了什么。
(我写这个答案正是因为我在我的代码库中发现了一个try/except/else,它引起了一个wtf时刻,迫使我做一些谷歌搜索)。
因此,无论我在哪里看到类似OP示例的代码:
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
else:
# do some more processing in non-exception case
return something
我更倾向于重构
try:
try_this(whatever)
except SomeException as the_exception:
handle(the_exception)
return # <1>
# do some more processing in non-exception case <2>
return something
<1>显式返回,清楚地表明,在异常情况下,我们已经完成了工作
<2>作为一个不错的小副作用,原来在else块中的代码被降低了一级。
你应该小心使用finally块,因为它与在try中使用else块不是一回事,除了。finally块将运行,而不管try的结果是什么。
In [10]: dict_ = {"a": 1}
In [11]: try:
....: dict_["b"]
....: except KeyError:
....: pass
....: finally:
....: print "something"
....:
something
正如每个人都注意到的那样,使用else块使您的代码更具可读性,并且仅在没有抛出异常时运行
In [14]: try:
dict_["b"]
except KeyError:
pass
else:
print "something"
....: