在Python中使用try-except-else是否是一种好习惯?

问题:在Python中使用try-except-else是否是一种好习惯?

在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

From time to time in Python, I see the block:

try:
   try_this(whatever)
except SomeException as exception:
   #Handle exception
else:
   return something

What is the reason for the try-except-else to exist?

I do not like that kind of programming, as it is using exceptions to perform flow control. However, if it is included in the language, there must be a good reason for it, isn’t it?

It is my understanding that exceptions are not errors, and that they should only be used for exceptional conditions (e.g. I try to write a file into disk and there is no more space, or maybe I do not have permission), and not for flow control.

Normally I handle exceptions as:

something = some_default_value
try:
    something = try_this(whatever)
except SomeException as exception:
    #Handle exception
finally:
    return something

Or if I really do not want to return anything if an exception happens, then:

try:
    something = try_this(whatever)
    return something
except SomeException as exception:
    #Handle exception

回答 0

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

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

甚至Python核心开发人员也将异常用于流控制,并且该样式已在语言中大量使用(即,迭代器协议使用StopIteration发出信号以终止循环)。

此外,try-except样式用于防止某些“跨越式”构造固有的竞争条件。例如,测试os.path.exists会导致信息在您使用时已过时。同样,Queue.full返回的信息可能已过时。在这种情况下,try-except-else样式将产生更可靠的代码。

“据我了解,异常不是错误,它们仅应用于特殊情况”

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

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

换句话说,您理解“exceptions是为了exceptions”是一条在其他语言中有意义的规则,但不适用于Python。

“但是,如果它本身包含在语言中,那一定有充分的理由,不是吗?”

除了帮助避免竞争条件外,异常对于在循环外拉出错误处理也非常有用。这是解释语言中的必要优化,这些语言通常不会具有自动循环不变的代码运动

另外,在通常情况下,异常可以大大简化代码,在正常情况下,处理问题的能力与问题发生的地方相距甚远。例如,通常有用于业务逻辑的顶级用户界面代码调用代码,而后者又调用低级例程。低级例程中出现的情况(例如数据库访问中唯一键的重复记录)只能以顶级代码处理(例如,要求用户提供与现有键不冲突的新键)。对此类控制流使用异常可以使中级例程完全忽略该问题,并将其与流控制的这一方面很好地分离。

这里有一篇关于异常必不可少的不错的博客文章

另外,请参见此堆栈溢出答案:异常真的是异常错误吗?

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

其他条款本身很有趣。它在没有exceptions的情况下运行,但是在最终条款之前。这是其主要目的。

如果没有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='')

最后,在尝试块中最常用的else子句是为了美化一些(在相同的缩进级别上对齐exceptions结果和非exceptions结果)。此用法始终是可选的,并非严格必要。

“I do not know if it is out of ignorance, but I do not like that kind of programming, as it is using exceptions to perform flow control.”

In the Python world, using exceptions for flow control is common and normal.

Even the Python core developers use exceptions for flow-control and that style is heavily baked into the language (i.e. the iterator protocol uses StopIteration to signal loop termination).

In addition, the try-except-style is used to prevent the race-conditions inherent in some of the “look-before-you-leap” constructs. For example, testing os.path.exists results in information that may be out-of-date by the time you use it. Likewise, Queue.full returns information that may be stale. The try-except-else style will produce more reliable code in these cases.

“It my understanding that exceptions are not errors, they should only be used for exceptional conditions”

In some other languages, that rule reflects their cultural norms as reflected in their libraries. The “rule” is also based in-part on performance considerations for those languages.

The Python cultural norm is somewhat different. In many cases, you must use exceptions for control-flow. Also, the use of exceptions in Python does not slow the surrounding code and calling code as it does in some compiled languages (i.e. CPython already implements code for exception checking at every step, regardless of whether you actually use exceptions or not).

In other words, your understanding that “exceptions are for the exceptional” is a rule that makes sense in some other languages, but not for Python.

“However, if it is included in the language itself, there must be a good reason for it, isn’t it?”

Besides helping to avoid race-conditions, exceptions are also very useful for pulling error-handling outside loops. This is a necessary optimization in interpreted languages which do not tend to have automatic loop invariant code motion.

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.

There is a nice blog post on the indispensibility of exceptions here.

Also, see this Stack Overflow answer: Are exceptions really for exceptional errors?

“What is the reason for the try-except-else to exist?”

The else-clause itself is interesting. It runs when there is no exception but before the finally-clause. That is its primary purpose.

Without the else-clause, the only option to run additional code before finalization would be the clumsy practice of adding the code to the try-clause. That is clumsy because it risks raising exceptions in code that wasn’t intended to be protected by the try-block.

The use-case of running additional unprotected code prior to finalization doesn’t arise very often. So, don’t expect to see many examples in published code. It is somewhat rare.

Another use-case for the else-clause is to perform actions that must occur when no exception occurs and that do not occur when exceptions are handled. For example:

recip = float('Inf')
try:
    recip = 1 / f(x)
except ZeroDivisionError:
    logging.info('Infinite result')
else:
    logging.info('Finite result')

Another example occurs in unittest runners:

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='')

Lastly, the most common use of an else-clause in a try-block is for a bit of beautification (aligning the exceptional outcomes and non-exceptional outcomes at the same level of indentation). This use is always optional and isn’t strictly necessary.


回答 1

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

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

else如果没有错误,将执行一个子句,通过不执行该代码try块中的代码,可以避免捕获意外错误。同样,捕获意外错误可能会隐藏错误。

例如:

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

“ try,except”套件有两个可选子句,elsefinally。所以实际上是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

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

finally

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中,大多数exceptions都是错误。

我们可以使用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

有评论者问:

假设您有一个可对外部API进行ping的方法,并且想在API包装器之外的类上处理异常,那么您是否只是从方法中的except子句中返回e,其中e是异常对象?

不,您不返回该异常,只需将其重新引发raise以保留堆栈跟踪即可。

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

我在这里详细回答

What is the reason for the try-except-else to exist?

A try block allows you to handle an expected error. The except block should only catch exceptions you are prepared to handle. If you handle an unexpected error, your code may do the wrong thing and hide bugs.

An else clause will execute if there were no errors, and by not executing that code in the try block, you avoid catching an unexpected error. Again, catching an unexpected error can hide bugs.

Example

For example:

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

The “try, except” suite has two optional clauses, else and finally. So it’s actually try-except-else-finally.

else will evaluate only if there is no exception from the try block. It allows us to simplify the more complicated code below:

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

so if we compare an else to the alternative (which might create bugs) we see that it reduces the lines of code and we can have a more readable, maintainable, and less buggy code-base.

finally

finally will execute no matter what, even if another line is being evaluated with a return statement.

Broken down with pseudo-code

It might help to break this down, in the smallest possible form that demonstrates all features, with comments. Assume this syntactically correct (but not runnable unless the names are defined) pseudo-code is in a function.

For example:

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

It is true that we could include the code in the else block in the try block instead, where it would run if there were no exceptions, but what if that code itself raises an exception of the kind we’re catching? Leaving it in the try block would hide that bug.

We want to minimize lines of code in the try block to avoid catching exceptions we did not expect, under the principle that if our code fails, we want it to fail loudly. This is a best practice.

It is my understanding that exceptions are not errors

In Python, most exceptions are errors.

We can view the exception hierarchy by using pydoc. For example, in Python 2:

$ python -m pydoc exceptions

or Python 3:

$ python -m pydoc builtins

Will give us the hierarchy. We can see that most kinds of Exception are errors, although Python uses some of them for things like ending for loops (StopIteration). This is Python 3’s hierarchy:

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

A commenter asked:

Say you have a method which pings an external API and you want to handle the exception at a class outside the API wrapper, do you simply return e from the method under the except clause where e is the exception object?

No, you don’t return the exception, just reraise it with a bare raise to preserve the stacktrace.

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

Or, in Python 3, you can raise a new exception and preserve the backtrace with exception chaining:

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

I elaborate in my answer here.


回答 2

Python不赞成将异常仅用于特殊情况的想法,实际上,习惯用法是“要求宽恕,而不是允许”。这意味着将异常作为流程控制的常规部分是完全可以接受的,并且实际上是受到鼓励的。

通常,这是一件好事,因为以这种方式工作有助于避免某些问题(显而易见的示例是,通常避免出现竞争条件),并且它倾向于使代码更具可读性。

假设您遇到这样一种情况,您需要处理一些用户输入,但是已经处理了默认输入。该try: ... except: ... else: ...结构使代码易于阅读:

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

注意优点。无需检查该值是否有效并单独对其进行分析,只需完成一次即可。代码也遵循更合理的顺序,首先是主代码路径,然后是“如果不起作用,请执行此操作”。

该示例自然有点虚构,但它显示了这种结构的情况。

Python doesn’t subscribe to the idea that exceptions should only be used for exceptional cases, in fact the idiom is ‘ask for forgiveness, not permission’. This means that using exceptions as a routine part of your flow control is perfectly acceptable, and in fact, encouraged.

This is generally a good thing, as working this way helps avoid some issues (as an obvious example, race conditions are often avoided), and it tends to make code a little more readable.

Imagine you have a situation where you take some user input which needs to be processed, but have a default which is already processed. The try: ... except: ... else: ... structure makes for very readable code:

try:
   raw_value = int(input())
except ValueError:
   value = some_processed_value
else: # no error occured
   value = process_value(raw_value)

Compare to how it might work in other languages:

raw_value = input()
if valid_number(raw_value):
    value = process_value(int(raw_value))
else:
    value = some_processed_value

Note the advantages. There is no need to check the value is valid and parse it separately, they are done once. The code also follows a more logical progression, the main code path is first, followed by ‘if it doesn’t work, do this’.

The example is naturally a little contrived, but it shows there are cases for this structure.


回答 3

在python中使用try-except-else是否是一种好习惯?

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

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

它表明您不太了解Python。此功能封装在dict.get方法中:

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

try/ except块是写什么都可以有效地在一行用原子方法执行的视觉上更多混乱和冗长的方式。在其他情况下,这是正确的。

但是,这并不意味着我们应该避免所有异常处理。在某些情况下,最好避免比赛条件。不要检查文件是否存在,只需尝试将其打开,然后捕获相应的IOError。为了简单起见,请尝试将其封装或分解为适当的名称。

阅读PythonZen,了解其中存在一些紧绷的原则,并且要警惕过于依赖其中任何一条语句的教条。

Is it a good practice to use try-except-else in python?

The answer to this is that it is context dependent. If you do this:

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

It demonstrates that you don’t know Python very well. This functionality is encapsulated in the dict.get method:

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

The try/except block is a much more visually cluttered and verbose way of writing what can be efficiently executing in a single line with an atomic method. There are other cases where this is true.

However, that does not mean that we should avoid all exception handling. In some cases it is preferred to avoid race conditions. Don’t check if a file exists, just attempt to open it, and catch the appropriate IOError. For the sake of simplicity and readability, try to encapsulate this or factor it out as apropos.

Read the Zen of Python, understanding that there are principles that are in tension, and be wary of dogma that relies too heavily on any one of the statements in it.


回答 4

请参见以下示例,该示例说明了有关try-except-else-finally的所有信息:

for i in range(3):
    try:
        y = 1 / i
    except ZeroDivisionError:
        print(f"\ti = {i}")
        print("\tError report: ZeroDivisionError")
    else:
        print(f"\ti = {i}")
        print(f"\tNo error report and y equals {y}")
    finally:
        print("Try block is run.")

实施它并获得:

    i = 0
    Error report: ZeroDivisionError
Try block is run.
    i = 1
    No error report and y equals 1.0
Try block is run.
    i = 2
    No error report and y equals 0.5
Try block is run.

See the following example which illustrate everything about try-except-else-finally:

for i in range(3):
    try:
        y = 1 / i
    except ZeroDivisionError:
        print(f"\ti = {i}")
        print("\tError report: ZeroDivisionError")
    else:
        print(f"\ti = {i}")
        print(f"\tNo error report and y equals {y}")
    finally:
        print("Try block is run.")

Implement it and come by:

    i = 0
    Error report: ZeroDivisionError
Try block is run.
    i = 1
    No error report and y equals 1.0
Try block is run.
    i = 2
    No error report and y equals 0.5
Try block is run.

回答 5

您应谨慎使用finally块,因为它与try中使用else块的功能不同,除了。无论try的结果如何,都将运行finally块。

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

You should be careful about using the finally block, as it is not the same thing as using an else block in the try, except. The finally block will be run regardless of the outcome of the try except.

In [10]: dict_ = {"a": 1}

In [11]: try:
   ....:     dict_["b"]
   ....: except KeyError:
   ....:     pass
   ....: finally:
   ....:     print "something"
   ....:     
something

As everyone has noted using the else block causes your code to be more readable, and only runs when an exception is not thrown

In [14]: try:
             dict_["b"]
         except KeyError:
             pass
         else:
             print "something"
   ....:

回答 6

每当您看到以下内容时:

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

Whenever you see this:

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

Or even this:

try:
    return 1 / x
except ZeroDivisionError:
    return None

Consider this instead:

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

回答 7

只是因为没有人发表过这一意见,我会说

避免使用else条款,因为大多数人都不熟悉这些条款try/excepts

与关键字tryexcept和和不同finally,该else子句的含义不言而喻。它的可读性较差。因为它不经常使用,所以它将导致阅读您的代码的人想要仔细检查文档,以确保他们了解正在发生的事情。

(我之所以写此答案,恰恰是因为我try/except/else在代码库中找到了a ,它导致了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>明确的回报清楚地表明,在exceptions情况下,我们已经完成工作

  • <2>作为一个很好的次要副作用,该else块中的代码以前经过了一个级别的确定。

Just because no-one else has posted this opinion, I would say

avoid else clauses in try/excepts because they’re unfamiliar to most people

Unlike the keywords try, except, and finally, the meaning of the else clause isn’t self-evident; it’s less readable. Because it’s not used very often, it’ll cause people that read your code to want to double-check the docs to be sure they understand what’s going on.

(I’m writing this answer precisely because I found a try/except/else in my codebase and it caused a wtf moment and forced me to do some googling).

So, wherever I see code like the OP example:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
else:
    # do some more processing in non-exception case
    return something

I would prefer to refactor to

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> explicit return, clearly shows that, in the exception case, we are finished working

  • <2> as a nice minor side-effect, the code that used to be in the else block is dedented by one level.


回答 8

这是我关于如何理解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

This is my simple snippet on howto understand try-except-else-finally block in Python:

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))

Let’s try div 1/1:

div(1, 1)
No Zero Division Error
Finally the division of 1/1 is done

Let’s try div 1/0

div(1, 0)
Zero Division Error detected
Finally the division of 1/0 is done

回答 9

OP,您是正确的。 在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美分。我必须走到后面,清理掉大学一年级开发人员写的很多代码,这些开发人员认为他们很聪明,并希望以超级严格,超级高效的方式编写代码,这只会使事情变得一团糟以尝试稍后阅读/修改。我每天对可读性进行投票,而星期日则两次。

OP, YOU ARE CORRECT. The else after try/except in Python is ugly. it leads to another flow-control object where none is needed:

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

A totally clear equivalent is:

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

This is far clearer than an else clause. The else after try/except is not frequently written, so it takes a moment to figure what the implications are.

Just because you CAN do a thing, doesn’t mean you SHOULD do a thing.

Lots of features have been added to languages because someone thought it might come in handy. Trouble is, the more features, the less clear and obvious things are because people don’t usually use those bells and whistles.

Just my 5 cents here. I have to come along behind and clean up a lot of code written by 1st-year out of college developers who think they’re smart and want to write code in some uber-tight, uber-efficient way when that just makes it a mess to try and read / modify later. I vote for readability every day and twice on Sundays.