简体   繁体   English

Python 使用异常进行控制流被认为不好?

[英]Python using exceptions for control flow considered bad?

All right,好的,

I've seen this multiple times in the past, but most recently with my question here .过去我已经多次看到这种情况,但最近我的问题在这里 So, I'm curious why this is the case, in python because generators use exceptions to indicate the end of the data.所以,我很好奇为什么会这样,在 python 中,因为生成器使用异常来指示数据的结尾。

If this is so bad for everyone using python, why does the language include it in what are considered fundamental control structures?如果这对使用 python 的每个人来说都如此糟糕,为什么该语言将它包含在被认为是基本控制结构中? For those who want to read the relevant PEP go here .对于那些想要阅读相关 PEP 的人,请访问这里

Because ending the generator is not a common event (I know it will always happen, but it only happens once ).因为结束生成器不是一个常见的事件(我知道它总是会发生,但它只会发生一次)。 Throwing the exception is considered expensive.抛出异常被认为是昂贵的。 If an event is going to succeed 99% of the time and fail 1%, using try/except can be much faster than checking if it's okay to access that data (it's easier to ask forgiveness than permission).如果一个事件有 99% 的成功率和 1% 的失败率,那么使用 try/except 比检查是否可以访问该数据要快得多(请求宽恕比请求许可更容易)。

There's also a bias against it since try/except blocks used like that can be very difficult to understand.也有对它的偏见,因为像这样使用的 try/except 块可能很难理解。 The flow control can be difficult to follow, while an if/else are more straightforward.流控制可能难以遵循,而 if/else 则更直接。 The try/except means you have to track the flow control of the statements inside the try and inside of the functions it calls (as they may throw the exception and it may propagate upwards. An if/else can only branch at the point when the statement is evaluated.在try /只是意味着你要跟踪它调用的函数的尝试内内的语句的流控制(因为它们可能会抛出异常,它可以向上传播。if / else语句可以在唯一分支机构在点时语句被评估。

There are times when using try/except is correct and times when if/else make more sense.有时使用 try/except 是正确的,有时使用 if/else 更有意义。 There are also performance costs associated with each of them.还有与它们中的每一个相关的性能成本。 Consider:考虑:

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

vs.对比

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

The first will be faster if key does not exist inside of a and will only be slightly (almost unnoticeable) slower if it does exist.如果 key 不存在于 a 中,第一个会更快,如果它确实存在,则只会稍微(几乎不明显)更慢。 The second will be faster if the key does exist, but will be much slower if it doesn't exist.如果键确实存在,第二个会更快,但如果它不存在,则会慢得多。 If key almost always exists, you go with the second.如果 key 几乎总是存在,则选择第二个。 Otherwise, the first works better.否则,第一个效果更好。

EDIT: Just a little thing to add about Python try/except that helps greatly with one of the readability problems.编辑:只需添加关于 Python try/except 的一点东西,这对解决可读性问题之一有很大帮助。

Consider reading from a file.考虑从文件中读取。

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

Now anything in the do stuff to the file can throw an exception and we'll catch it.现在do stuff to the file任何do stuff to the file都可能引发异常,我们将捕获它。 Commonly, you try to keep as little code as possible in the try for this reason.通常,出于这个原因,您会尝试在 try 中保留尽可能少的代码。 Python has an optional else clause for the try that will only be run if the try ran to completion without hitting an exception. 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()

In this case, you would not have any of the readability problems since only one statement is in the try;在这种情况下,您不会遇到任何可读性问题,因为 try 中只有一个语句; it is a python standard library function call and you're catching only specific exceptions.它是一个 python 标准库函数调用,您只捕获特定的异常。

Because exceptions are raised up the stack, they are appropriate for some cases where you want code that uses other code to be able to catch the exception.因为异常是在堆栈上引发的,所以它们适用于某些情况,即您希望使用其他代码的代码能够捕获异常。 For example, consider the case where you want to create an iterator that uses part of another iterator more "manually", it's valuable to have an exception you can catch from higher up the stack and insert your own logic.例如,考虑到您想要创建一个迭代器的情况,该迭代器更“手动”地使用另一个迭代器的一部分,拥有一个可以从堆栈的更高层捕获并插入您自己的逻辑的异常是很有价值的。

Using try blocks for flow control all the time could produce code like this:一直使用try块进行流量控制可能会产生如下代码:

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

Performance concerns aside, this is not very elegant.撇开性能问题不谈,这不是很优雅。 So, sometimes it's perfectly appropriate to use try , but not for everything.因此,有时使用try非常合适,但并非适用于所有情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM