在Python中使用异常控制流程是否被认为是不好的?

21 投票
3 回答
11068 浏览
提问于 2025-04-17 01:07

好的,

我以前见过很多次这种情况,最近在我这里的问题中又碰到了。所以,我很好奇为什么在Python中会这样,因为生成器用异常来表示数据的结束。

如果这对所有使用Python的人来说都不好,那为什么这个语言会把它放在被认为是基本控制结构的部分呢?想要了解相关内容的人可以查看这个PEP,点这里

3 个回答

0

一直使用try块来控制程序的流程可能会导致代码变成这样:

try:
  # stuff
  try:
    if userCondition:
      raise NeedToDoSomethingElseException
    try:
      # stuff
    except NeedToDoSomethingElseException:
      # other stuff
  except NeedToDoSomethingElseException:
    # other stuff
except NeedToDoSomethingElseException:
  # other stuff

除了性能问题,这样的写法也不太优雅。所以,有时候使用try是合适的,但并不是所有情况下都要用。

1

因为异常是从调用的代码层层抛出来的,所以在某些情况下,它们非常适合让使用其他代码的部分能够捕捉到这些异常。举个例子,假设你想创建一个迭代器,而这个迭代器需要更“手动”地使用另一个迭代器的部分,这时候能够从更高的层次捕捉到异常就很有用了,这样你就可以插入自己的处理逻辑。

15

因为结束生成器的情况并不常见(我知道它总会发生,但只发生一次)。抛出异常被认为是比较耗费资源的。如果一个事件有99%的成功率和1%的失败率,使用try/except可能比检查是否可以访问数据要快得多(有时候,事后求饶比事先请求许可更简单)。

另外,使用try/except的方式也有一些偏见,因为这样使用的try/except块可能很难理解。控制流程可能会很复杂,而if/else则更直接。使用try/except意味着你需要跟踪try里面的语句的控制流程,以及它调用的函数里面的控制流程(因为这些函数可能会抛出异常,异常可能会向上冒泡)。而if/else只在语句被评估时分支。

有时候使用try/except是正确的,有时候使用if/else更合适。它们各自也有性能上的差异。考虑一下:

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

a = <some dictionary>
try:
    print a[key]
except KeyError:
    pass

如果key在a里面不存在,第一种方式会更快;如果存在,速度只会稍微慢一点(几乎感觉不到)。第二种方式如果key存在会更快,但如果不存在就会慢很多。如果key几乎总是存在,那就用第二种方式。否则,第一种方式更好。

补充一下关于Python中try/except的小知识,这有助于解决可读性的问题。

考虑从文件中读取数据。

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中的任何操作都可能抛出异常,我们会捕获这些异常。通常情况下,你会尽量在try中保持代码量尽可能少,这就是原因。Python有一个可选的else子句,只有在try顺利完成且没有抛出异常时才会执行。

f = None
try:
    f = open(filename, 'r')
except (IOError, OSError):
    pass
else:
    ... do stuff to the file ...
finally:
    if f:
        f.close()

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

撰写回答