【问题标题】:Python using exceptions for control flow considered bad?Python对控制流使用异常被认为是不好的?
【发布时间】:2011-11-08 14:43:09
【问题描述】:

好吧,

我过去曾多次看到这种情况,但最近一次是my question here。所以,我很好奇为什么会出现这种情况,在 python 中,因为生成器使用异常来指示数据的结束。

如果这对使用 python 的每个人来说都如此糟糕,为什么该语言将它包含在被认为是基本控制结构中?对于那些想阅读相关PEP的人go here。

【问题讨论】:

  • 实用胜过纯洁。

标签: python try-catch generator


【解决方案1】:

因为结束生成器不是一个常见的事件(我知道它总是会发生,但它只会发生一次)。抛出异常被认为是昂贵的。如果一个事件将在 99% 的时间内成功并在 1% 的情况下失败,那么使用 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 几乎总是存在,则选择第二个。否则,第一个效果更好。

编辑:只需添加一点关于 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 中的任何内容都可能引发异常,我们会捕获它。通常,出于这个原因,您尝试在尝试中保留尽可能少的代码。 Python 有一个可选的 else 子句用于 try,只有在 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 标准库函数调用,您只捕获特定的异常。

【讨论】:

  • 但是性能不应该决定什么是惯用的,什么不是(相反,它应该是相反的——实现应该而且实际上是针对惯用语进行优化的)。好吧,除了在 C++ 中是 ;)
  • 一个很好的答案。我完全同意你的看法,尽管我很好奇你为什么不包含类似“代码是为程序员而不是计算机编写的。因此它应该是为人编写的,异常作为控制流是模棱两可的”。我认为性能,尤其是在 python 中,每天比代码库的可读性要小得多。
  • @Spencer Rathbun 主要是因为我有点语无伦次地混淆了两点(一个是关于性能,一个是关于可读性)。
  • @Jonathan Sternberg 啊,这就解释了。我会接受它作为答案。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    一直使用try 块进行流量控制可能会产生如下代码:

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

    抛开性能问题,这不是很优雅。因此,有时使用try 是完全合适的,但并非所有情况都适用。

    【讨论】:

    • 不是一个真正的论点,因为if 风格会(并且将会)发生同样的情况。
    • 所以在你的if 块中,你有等效的triggerElse 语句散布在各处?
    猜你喜欢
    • 1970-01-01
    • 2010-10-18
    • 2011-12-29
    • 1970-01-01
    • 2018-08-11
    • 2015-08-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多