在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中使用try-except-else是一个好习惯吗?

答案是,这取决于上下文。如果你这样做:

d = dict()
try:
    item = d['item']
except KeyError:
    item = 'default'

这说明您不是很了解Python。这个功能封装在字典中。获得方法:

item = d.get('item', 'default')

try/except块是一种视觉上更加混乱和冗长的方式,它可以用原子方法在一行中有效地执行。在其他情况下也是如此。

然而,这并不意味着我们应该避免所有的异常处理。在某些情况下,最好避免竞争条件。不要检查文件是否存在,只是尝试打开它,并捕获相应的IOError。出于简单性和可读性的考虑,请尝试将其封装或适当地提取出来。

阅读Python的禅宗,了解有一些原则处于紧张状态,并警惕过分依赖其中任何一条陈述的教条。

其他回答

当你看到这个:

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

哦,你是对的。在Python中,try/except后面的else很难看。它指向另一个不需要的流控制对象:

try:
    x = blah()
except:
    print "failed at blah()"
else:
    print "just succeeded with blah"

一个完全等价的词是:

try:
    x = blah()
    print "just succeeded with blah"
except:
    print "failed at blah()"

这比else子句清楚得多。try/except之后的else并不经常被写入,因此需要花一些时间来确定其含义。

仅仅因为你能做一件事,并不意味着你应该做一件事。

许多特性被添加到语言中,因为有人认为它可能会派上用场。问题是,功能越多,事物就越不清晰和明显,因为人们通常不使用那些花哨的东西。

这只是我的5分钱。我必须跟在后面,清理许多由大学一年级的开发人员编写的代码,这些开发人员自认为很聪明,想以一种超级紧凑、超级高效的方式编写代码,但这样做只会让以后尝试和阅读/修改时变得一团糟。我每天都为可读性投票,周日两次。

try-except-else存在的原因是什么?

try块允许您处理预期的错误。except块应该只捕获准备处理的异常。如果您处理一个意外错误,您的代码可能会做错误的事情并隐藏bug。

如果没有错误,则执行else子句,并且通过不在try块中执行该代码,可以避免捕获意外错误。同样,捕捉意外错误可以隐藏错误。

例子

例如:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
else:
    return something

“try, except”套件有两个可选子句,else和finally。实际上是try-except-else-finally。

Else只在try块中没有异常时才计算。它允许我们简化下面更复杂的代码:

no_error = None
try:
    try_this(whatever)
    no_error = True
except SomeException as the_exception:
    handle(the_exception)
if no_error:
    return something

因此,如果我们将else与替代方案(可能会产生错误)进行比较,我们会发现它减少了代码行数,我们可以拥有一个更可读、更可维护、更少错误的代码库。

最后

Finally无论如何都会执行,即使另一行正在用return语句求值。

用伪代码分解

用尽可能小的形式演示所有特性,并加上注释,可能会有所帮助。假设这个伪代码在语法上是正确的(但除非定义了名称,否则是不可运行的)。

例如:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle_SomeException(the_exception)
    # Handle a instance of SomeException or a subclass of it.
except Exception as the_exception:
    generic_handle(the_exception)
    # Handle any other exception that inherits from Exception
    # - doesn't include GeneratorExit, KeyboardInterrupt, SystemExit
    # Avoid bare `except:`
else: # there was no exception whatsoever
    return something()
    # if no exception, the "something()" gets evaluated,
    # but the return will not be executed due to the return in the
    # finally block below.
finally:
    # this block will execute no matter what, even if no exception,
    # after "something" is eval'd but before that value is returned
    # but even if there is an exception.
    # a return here will hijack the return functionality. e.g.:
    return True # hijacks the return in the else clause above

确实,我们可以将else块中的代码包含在try块中,如果没有异常,它将在其中运行,但如果该代码本身引发了我们正在捕获的那种异常呢?将它留在try块中会隐藏该错误。

我们希望尽量减少try块中的代码行数,以避免捕获我们没有预料到的异常,原则是如果代码失败,我们希望它大声失败。这是一个最佳实践。

我的理解是异常不是错误

在Python中,大多数异常都是错误。

我们可以使用pydoc查看异常层次结构。例如,在Python 2中:

$ python -m pydoc exceptions

或Python 3:

$ python -m pydoc builtins

会给出层次结构。我们可以看到大多数类型的Exception都是错误,尽管Python将其中一些用于结束for循环(StopIteration)等事情。这是Python 3的层次结构:

BaseException
    Exception
        ArithmeticError
            FloatingPointError
            OverflowError
            ZeroDivisionError
        AssertionError
        AttributeError
        BufferError
        EOFError
        ImportError
            ModuleNotFoundError
        LookupError
            IndexError
            KeyError
        MemoryError
        NameError
            UnboundLocalError
        OSError
            BlockingIOError
            ChildProcessError
            ConnectionError
                BrokenPipeError
                ConnectionAbortedError
                ConnectionRefusedError
                ConnectionResetError
            FileExistsError
            FileNotFoundError
            InterruptedError
            IsADirectoryError
            NotADirectoryError
            PermissionError
            ProcessLookupError
            TimeoutError
        ReferenceError
        RuntimeError
            NotImplementedError
            RecursionError
        StopAsyncIteration
        StopIteration
        SyntaxError
            IndentationError
                TabError
        SystemError
        TypeError
        ValueError
            UnicodeError
                UnicodeDecodeError
                UnicodeEncodeError
                UnicodeTranslateError
        Warning
            BytesWarning
            DeprecationWarning
            FutureWarning
            ImportWarning
            PendingDeprecationWarning
            ResourceWarning
            RuntimeWarning
            SyntaxWarning
            UnicodeWarning
            UserWarning
    GeneratorExit
    KeyboardInterrupt
    SystemExit

一位评论者问道:

假设你有一个方法可以ping外部API,你想在API包装器之外的类中处理异常,你只是从方法的except子句中返回e,其中e是异常对象吗?

不,您不返回异常,只是用一个简单的提升来重新提升它以保留堆栈跟踪。

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    raise

或者,在Python 3中,你可以引发一个新的异常,并使用异常链接保存回溯:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    raise DifferentException from the_exception

我在这里详细说明我的答案。

在python中使用try-except-else是一个好习惯吗?

答案是,这取决于上下文。如果你这样做:

d = dict()
try:
    item = d['item']
except KeyError:
    item = 'default'

这说明您不是很了解Python。这个功能封装在字典中。获得方法:

item = d.get('item', 'default')

try/except块是一种视觉上更加混乱和冗长的方式,它可以用原子方法在一行中有效地执行。在其他情况下也是如此。

然而,这并不意味着我们应该避免所有的异常处理。在某些情况下,最好避免竞争条件。不要检查文件是否存在,只是尝试打开它,并捕获相应的IOError。出于简单性和可读性的考虑,请尝试将其封装或适当地提取出来。

阅读Python的禅宗,了解有一些原则处于紧张状态,并警惕过分依赖其中任何一条陈述的教条。

“我不知道这是不是出于无知,但我不喜欢这样 这是一种编程,因为它使用异常来执行流控制。”

在Python世界中,使用异常进行流控制是很常见和正常的。

即使是Python的核心开发人员也会使用异常来进行流控制,并且这种风格已经深深地嵌入到语言中(即迭代器协议使用StopIteration来发出循环终止的信号)。

此外,try-except-样式用于防止某些“三思而后行”构造中固有的竞争条件。例如,测试os.path.exists所得到的信息在您使用它时可能已经过时。同样地,队列。完整返回可能过期的信息。在这些情况下,try-except-else样式将生成更可靠的代码。

“根据我的理解,异常不是错误,它们应该只是 用于特殊情况"

在其他一些语言中,这一规则反映了他们的文化规范,就像他们的图书馆所反映的那样。该“规则”部分也是基于这些语言的性能考虑。

Python文化规范有些不同。在许多情况下,您必须为控制流使用异常。此外,在Python中使用异常不会像在一些编译语言中那样减慢周围的代码和调用代码(即CPython已经在每一步实现了异常检查的代码,不管你是否实际使用异常)。

换句话说,您理解的“异常适用于异常”的规则在其他一些语言中是有意义的,但对Python则不然。

然而,如果它包含在语言本身,就必须有一个 这是很好的理由,不是吗?”

除了有助于避免竞态条件,异常对于在外部循环中进行错误处理也非常有用。在解释性语言中,这是一个必要的优化,因为解释性语言往往没有自动循环不变的代码运动。

Also, exceptions can simplify code quite a bit in common situations where the ability to handle an issue is far removed from where the issue arose. For example, it is common to have top level user-interface code calling code for business logic which in turn calls low-level routines. Situations arising in the low-level routines (such as duplicate records for unique keys in database accesses) can only be handled in top-level code (such as asking the user for a new key that doesn't conflict with existing keys). The use of exceptions for this kind of control-flow allows the mid-level routines to completely ignore the issue and be nicely decoupled from that aspect of flow-control.

这里有一篇关于例外的必要性的不错的博客文章。

另外,请参阅Stack Overflow的回答:异常真的是针对异常错误的吗?

“除了别的尝试存在的原因是什么?”

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-block中使用else子句最常见的用途是进行一些美化(将异常结果和非异常结果对齐在同一缩进级别)。这种用法总是可选的,并不是严格必要的。