关于try catch:Python使用异常控制流被认为是坏的?

Python using exceptions for control flow considered bad?

行,

我过去曾多次看过这个,但最近我的问题在这里。 所以,我很好奇为什么会这样,在python中,因为生成器使用异常来指示数据的结束。

如果对于每个使用python的人都这么糟糕,为什么语言将它包含在被认为是基本控制结构的内容中呢? 对于那些想要阅读相关PEP的人,请访问此处。


因为结束生成器不是一个常见的事件(我知道它总会发生,但它只发生一次)。抛出异常被认为是昂贵的。如果一个事件在99%的时间内成功并且失败1%,那么使用try / except可能比检查是否可以访问该数据要快得多(请求宽恕比允许更容易)。

还有一个偏见,因为尝试/除了这样使用的块可能很难理解。流控制可能难以遵循,而if / else更直接。 try / except意味着你必须跟踪try中的语句和它调用的函数内部的语句的流控制(因为它们可能抛出异常并且它可能向上传播.if / else只能分支在声明被评估。

有时候使用try / except是正确的,以及if / else更有意义的时候。还存在与每个相关的性能成本。考虑:

1
2
3
a = <some dictionary>
if key in a:
    print a[key]

1
2
3
4
5
a = <some dictionary>
try:
    print a[key]
except KeyError:
    pass

如果密钥不存在于a中,则第一个会更快,如果它确实存在,则只会稍微(几乎不可察觉)。如果密钥确实存在,则第二个将更快,但如果密钥不存在则会慢得多。如果密钥几乎总是存在,那么你选择第二个。否则,第一个更好。

编辑:只是添加一些关于Python try /的东西,除了这有助于解决其中一个可读性问题。

考虑从文件中读取。

1
2
3
4
5
6
7
8
9
10
f = None
try:
    f = open(filename, 'r')
    ... do stuff to the file ...
except (IOError, OSError):
    # I can never remember which one of these Python throws...
    ... handle exception ...
finally:
    if f:
        f.close()

现在do stuff to the file中的任何内容都可以抛出异常而我们会抓住它。通常,您尝试在尝试中保留尽可能少的代码,因为这个原因。 Python为try提供了一个可选的else子句,只有在尝试运行完成而没有遇到异常时才会运行。

1
2
3
4
5
6
7
8
9
10
f = None
try:
    f = open(filename, 'r')
except (IOError, OSError):
    pass
else:
    ... do stuff to the file ...
finally:
    if f:
        f.close()

在这种情况下,您不会有任何可读性问题,因为在try中只有一个语句;它是一个python标准库函数调用,你只捕获特定的异常。


使用try块进行流量控制始终可以生成如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
try:
  # stuff
  try:
    if userCondition:
      throw NeedToDoSomethingElseException
    try:
      # stuff
    except NeedToDoSomethingElseException:
      # other stuff
  except NeedToDoSomethingElseException:
    # other stuff
except NeedToDoSomethingElseException:
  # other stuff

除了性能问题,这不是很优雅。因此,有时使用try是完全合适的,但不适用于所有内容。


因为异常是在堆栈中引发的,所以它们适用于您希望使用其他代码的代码能够捕获异常的某些情况。例如,考虑你想要创建一个更"手动"使用另一个迭代器的一部分的迭代器的情况,有一个异常你可以从堆栈的高处捕获并插入你自己的逻辑是很有价值的。