关于异常:在Python中使用try except else是一个好的实践吗?

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

在python中,我经常看到块:

1
2
3
4
5
6
try:
   try_this(whatever)
except SomeException as exception:
   #Handle exception
else:
   return something

除了存在其他尝试的原因是什么?

我不喜欢这种编程,因为它使用异常来执行流控制。但是,如果它包含在语言中,那么它一定有一个很好的理由,不是吗?

据我所知,异常不是错误,它们只应用于异常情况(例如,我试图将文件写入磁盘,但空间不足,或者我没有权限),而不用于流控制。

通常我处理异常的方式是:

1
2
3
4
5
6
7
something = some_default_value
try:
    something = try_this(whatever)
except SomeException as exception:
    #Handle exception
finally:
    return something

或者如果我真的不想返回任何异常情况,那么:

1
2
3
4
5
try:
    something = try_this(whatever)
    return something
except SomeException as exception:
    #Handle exception

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

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

即使是Python核心开发人员也会使用流控制的异常,并且这种风格严重影响到语言(即迭代器协议使用StopIteration来信号循环终止)。

此外,Try-Except样式用于防止某些"在跳跃前查看"构造中固有的竞争条件。例如,测试os.path.exists会导致信息在使用时可能过时。同样,queue.full返回可能过时的信息。在这些情况下,除其他之外的try样式将生成更可靠的代码。

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

在其他一些语言中,这条规则反映了他们图书馆反映的文化规范。"规则"也部分基于这些语言的性能考虑。

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

换句话说,您对"例外适用于例外"的理解在某些其他语言中是有意义的,但对于Python则不是。

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

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

此外,在处理问题的能力与问题发生的位置相差甚远的常见情况下,异常可以大大简化代码。例如,有顶级用户界面代码调用业务逻辑的代码是很常见的,而业务逻辑又调用低级例程。低级例程中出现的情况(例如,数据库访问中唯一键的重复记录)只能用顶级代码处理(例如,向用户请求一个不与现有键冲突的新键)。对此类控制流使用异常允许中间层例程完全忽略该问题,并与流控制的这一方面很好地分离。

这里有一篇关于异常不可分割的好博客文章。

另外,请参阅此堆栈溢出回答:异常是否真的是异常错误的异常?

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

else子句本身很有趣。它在finally子句之前运行。这是它的主要目的。

如果没有else子句,在完成之前运行附加代码的唯一选项是将代码添加到try子句中的笨拙做法。那是笨拙的,因为它有风险在代码中引发不受try块保护的异常。

在完成之前运行额外的未保护代码的用例并不经常出现。所以,不要期望在已发布的代码中看到许多示例。这有点罕见。

else子句的另一个用例是执行在没有异常发生时必须发生的操作,以及在处理异常时不发生的操作。例如:

1
2
3
4
5
6
7
   recip = float('Inf')
   try:
       recip = 1 / f(x)
   except ZeroDivisionError:
       logging.info('Infinite result')
   else:
       logging.info('Finite result')

最后,try块中else子句最常见的用法是美化(将异常结果和非异常结果在同一缩进级别上对齐)。这种用法总是可选的,并不是严格必要的。


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

这允许你把一块try预期的错误。在catch块异常except只,你应该准备处理。如果你把一个意外的错误代码的错误,你可以隐藏和错误的事情。

如果有一else条款将被执行的错误,这是由代码在不被你抓住一块try,避免意外的错误。一次意外的错误,可以捕捉隐藏的错误。

的例子

例如:

1
2
3
4
5
6
try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
else:
    return something

"试试吧,除了有"二套房elsefinally可选条款。所以这是真的try-except-else-finally

如果有一个else将评估在从try异常块。它允许更复杂的代码来简化为以下:

1
2
3
4
5
6
7
8
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

finally将运行在另一个问题是,即使是为在线评估与return语句。

下的伪码与破碎

它可能帮助打破这种下降是可能的,在smallest demonstrates型全功能,与评论。这是syntactically(但不正确的名称是runnable除非定义)中的伪码的功能。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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例外我们没有期望的人,如果我们的代码是根据原则的失败,我们要大声,它失败。这是一个最佳实践。

It is my understanding that exceptions are not errors

Python中的例外,大多数都是错误的。

我们可以利用pydoc异常层次的视图。例如,Python中的2

1
$ python -m pydoc exceptions

或:Python 3

1
$ python -m pydoc builtins

该法将给美国。我们可以看到这是最错误的种类(Exception,虽然他们使用的是Python的一些事情的结局for样环(StopIteration)。这是一个Python 3′s法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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

这个人问道:

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?

在,你将reraise异常,只是它与裸raise保护的程序。

1
2
3
4
5
try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    raise

或者,你可以在Python中的3,保持新的异常和异常的回溯与链接:

1
2
3
4
5
try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
    raise DifferentException from the_exception

我在我的精心安排的答案在这里。


python不同意例外只应用于例外情况的观点,事实上,这个成语是"请求原谅,而不是许可"。这意味着将异常作为流程控制的常规部分是完全可以接受的,事实上,这是鼓励的。

这通常是一件好事,因为这样做有助于避免一些问题(作为一个明显的例子,竞态条件经常被避免),而且它会使代码更易于阅读。

假设您有这样一种情况,您接受一些需要处理的用户输入,但是有一个已经处理的默认值。try: ... except: ... else: ...结构使得代码非常可读:

1
2
3
4
5
6
try:
   raw_value = int(input())
except ValueError:
   value = some_processed_value
else: # no error occured
   value = process_value(raw_value)

与它在其他语言中的工作方式相比:

1
2
3
4
5
raw_value = input()
if valid_number(raw_value):
    value = process_value(int(raw_value))
else:
    value = some_processed_value

注意优点。无需检查该值是否有效并单独分析,只需执行一次。代码也遵循更合理的顺序,主代码路径是第一个,然后是"如果它不起作用,就这样做"。

这个例子自然有点做作,但它显示了这种结构的一些情况。


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

答案是它依赖于上下文。如果你这样做:

1
2
3
4
5
d = dict()
try:
    item = d['item']
except KeyError:
    item = 'default'

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

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

tryexcept块是一种更为直观、复杂的方法,它可以用原子方法在一行中高效地执行。还有其他情况是这样的。

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

阅读Python的禅宗,了解存在紧张的原则,并警惕过于依赖其中任何一个陈述的教条。


下面的例子说明,国有企业的一切努力-除其他部件:——

1
2
3
4
5
6
7
8
9
10
11
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.")

器具及作者:吃它

1
2
3
4
5
6
7
8
9
    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.


在使用finally块时应该小心,因为它与在try中使用else块不是一回事,除非。无论尝试的结果如何,finally块都将运行except。

1
2
3
4
5
6
7
8
9
10
In [10]: dict_ = {"a": 1}

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

正如每个人都注意到的,使用else块会使代码更可读,并且只在不引发异常时运行

1
2
3
4
5
6
7
In [14]: try:
             dict_["b"]
         except KeyError:
             pass
         else:
             print"something"
   ....:


时,你看到这个:

1
2
3
4
5
6
try:
    y = 1 / x
except ZeroDivisionError:
    pass
else:
    return y

或者甚至这个:

1
2
3
4
try:
    return 1 / x
except ZeroDivisionError:
    return None

考虑这个:。

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


这是我的简单的代码了解如何在线-其他-地方试块:理解Python中的

1
2
3
4
5
6
7
8
9
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。

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

Let’s try DIV/0 1

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

哦,你是对的。在python中,try/except之后的else是丑陋的。它会导致另一个不需要的流控制对象:

1
2
3
4
5
6
try:
    x = blah()
except:
    print"failed at blah()"
else:
    print"just succeeded with blah"

一个完全明确的等价物是:

1
2
3
4
5
try:
    x = blah()
    print"just succeeded with blah"
except:
    print"failed at blah()"

这比其他条款要清楚得多。Try/Except之后的else并不经常被编写,因此需要花一点时间来了解其含义。

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

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

这里只有我的5美分。我必须跟在后面,清理大学一年级的开发人员编写的许多代码,他们认为自己很聪明,并且希望以某种超紧凑、超高效的方式编写代码,而这只会使以后尝试和读取/修改变得一团糟。我每天投票支持可读性,星期天两次。