在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"
   ....:

当你看到这个:

try:
    y = 1 / x
except ZeroDivisionError:
    pass
else:
    return y

甚至是这样:

try:
    return 1 / x
except ZeroDivisionError:
    return None

不妨考虑一下这个问题:

import contextlib
with contextlib.suppress(ZeroDivisionError):
    return 1 / x