如果首先可以避免异常,最好“尝试”某些东西并捕获异常或测试?

时间:2011-09-29 23:55:30

标签: python exception-handling if-statement try-catch pep

我应该测试if某些内容是否有效,或仅try来执行此操作并捕获异常?

  • 是否有任何可靠的文件说首选方式?
  • 还有一种方式 pythonic

例如,我应该:

if len(my_list) >= 4:
    x = my_list[3]
else:
    x = 'NO_ABC'

或者:

try:
    x = my_list[3]
except IndexError:
    x = 'NO_ABC'

一些想法......
PEP 20说:

  

错误不应该默默地传递。
  除非明确地保持沉默。

使用try代替if是否应该被解释为默默传递的错误?如果是这样,您是否通过这种方式使用它来明确地对其进行静音,从而使其正常?


指的是你只能单向做事的情况;例如:

try:
    import foo
except ImportError:
    import baz

8 个答案:

答案 0 :(得分:138)

如果结果为try/except,您应该更喜欢if/else

  • 加速(例如通过防止额外查找)
  • 清洁代码(线条少/易于阅读)

通常,这些都是相辅相成的。


速度起坐

在尝试通过以下方式在长列表中查找元素的情况下:

try:
    x = my_list[index]
except IndexError:
    x = 'NO_ABC'

try,当index可能在列表中并且通常不引发IndexError时,除了是最佳选项。这样您就无需if index < len(mylist)进行额外查找。

Python鼓励使用异常,您处理的 是来自Dive Into Python的短语。您的示例不仅处理异常(优雅地),而不是让它静默传递,而且仅在未找到索引的例外情况下发生异常(因此例外!)。


清洁代码

官方Python文档提到EAFP更容易请求宽恕而不是权限Rob Knight注意捕获错误而不是避免错误,可以使代码更清晰,更易于阅读。他的例子就是这样说的:

更糟糕(LBYL'在你跳跃之前看')

#check whether int conversion will raise an error
if not isinstance(s, str) or not s.isdigit:
    return None
elif len(s) > 10:    #too many digits for int conversion
    return None
else:
    return int(str)

更好(EAFP:更容易请求宽恕而非许可)

try:
    return int(str)
except (TypeError, ValueError, OverflowError): #int conversion failed
    return None

答案 1 :(得分:17)

在这种特殊情况下,你应该完全使用别的东西:

x = myDict.get("ABC", "NO_ABC")

通常,但是:如果您希望测试频繁失败,请使用if。如果测试相对于仅尝试操作而言是昂贵的,并且如果失败则捕获异常,请使用try。如果这些条件都不适用,那么更容易阅读。

答案 2 :(得分:7)

如果在做之前检查某些事情是否会失败是微不足道的,那么你应该赞成这一点。毕竟,构建异常(包括相关的追溯)需要时间。

例外情况应该用于:

  1. 意外的事情,或......
  2. 您需要跳过多个逻辑级别的内容(例如break无法让您足够远的地方),或者......
  3. 你不确切知道将要提前处理异常的事情,或者......
  4. 提前检查失败的事情是昂贵的(相对于只是尝试操作)

  5. 请注意,通常情况下,真正的答案是“不” - 例如,在您的第一个示例中,您真正应该做的只是使用.get()来提供默认值:

    x = myDict.get('ABC', 'NO_ABC')
    

答案 3 :(得分:7)

直接使用tryexcept而不是if后卫,如果有可能出现竞争条件,总是 。例如,如果要确保存在目录,请不要这样做:

import os, sys
if not os.path.isdir('foo'):
  try:
    os.mkdir('foo')
  except OSError, e
    print e
    sys.exit(1)

如果另一个线程或进程在isdirmkdir之间创建了目录,您将退出。相反,这样做:

import os, sys, errno
try:
  os.mkdir('foo')
except OSError, e
  if e.errno != errno.EEXIST:
    print e
    sys.exit(1)

只有在无法创建'foo'目录时才会退出。

答案 4 :(得分:4)

  

应该使用try而不是if被解释为默认传递的错误吗?如果是这样,您是否通过这种方式使用它来明确地对其进行静音,从而使其正常?

使用try确认错误可能会通过,这与静默通过相反。使用except导致它根本无法通过。

try: except:逻辑更复杂的情况下,首选使用if: else:。简单比复杂更好;复杂比复杂更好;要求宽恕比获得许可更容易。

什么“错误不应该默默地传递”是警告,代码可以引发你知道的异常,以及你的设计承认可能性,但你没有设计的方式来处理例外。在我看来,明确地消除错误会在pass块中执行类似except的操作,这应该只是理解“无所事事”确实是特定的错误处理情况。 (这是我觉得可能真的需要在编写良好的代码中发表评论的少数几次之一。)

但是,在您的特定示例中,两者都不合适:

x = myDict.get('ABC', 'NO_ABC')

每个人都指出这一点的原因 - 即使你承认你想要理解一般,并且无法提出一个更好的例子 - 在很多情况下实际存在相同的副步骤,并寻找他们是解决问题的第一步。

答案 5 :(得分:4)

正如其他帖子所述,这取决于具体情况。使用try / except会有一些危险,而不是提前检查数据的有效性,尤其是在大型项目中使用时。

  • try块中的代码可能有机会在异常被捕获之前造成各种破坏 - 如果您事先主动检查if语句,则可以避免这种情况。
  • 如果你的try块中调用的代码引发了一个常见的异常类型,比如TypeError或ValueError,你可能实际上并没有捕到你期望捕获的同一个异常 - 它可能是其他一些在之前或之后引发同一异常类的东西甚至到达可能引发异常的行。
例如,假设你有:

try:
    x = my_list[index_list[3]]
except IndexError:
    x = 'NO_ABC'

IndexError没有说明在尝试获取index_list或my_list元素时是否发生。

答案 6 :(得分:1)

每当您使用try/except进行控制时,请问自己:

  1. 很容易看到try块何时成功以及何时失败?
  2. 您是否知道try块内的所有副作用?
  3. 您是否意识到try块引发异常的所有 情况?
  4. 如果try块的实现发生了变化,您的控制流是否仍将按预期运行?

如果对这些问题中的一个或多个的回答为“否”,则可能会有很多宽恕的要求;


一个例子。 我最近在一个更大的项目中看到了如下代码:

try:
    y = foo(x)
except ProgrammingError:
    y = bar(x)

与程序员交谈时,发现原来的控制流程是:

  

如果x是整数,则执行y = foo(x)。

     

如果x是整数列表,则y = bar(x)。

之所以可行,是因为foo进行了数据库查询,如果x是整数,则查询成功,如果ProgrammingError是列表,则抛出x

>

在这里使用try/except是一个错误的选择:

  1. 异常的名称ProgrammingError并没有给出实际的问题(x不是整数)。这使得很难弄清发生了什么。
  2. 在数据库调用期间引发ProgrammingError,这不必要地浪费时间。如果事实证明foo会在引发异常或更改某些其他系统的状态之前将某些内容写入数据库,那么事情将变得非常可怕。
  3. 尚不清楚是否每个ProgrammingError都是由x作为列表引起的。例如,假设foo的数据库查询中有一个错字。这也可能会引发ProgrammingError。结果是,如果bar(x)是整数,那么现在也将调用x。这可能会引发神秘的异常或产生无法预料的结果。
  4. try/except块为foo的所有将来实现添加了要求。每当我们更改foo时,我们现在都必须考虑它如何处理列表,并确保它抛出ProgrammingError而不抛出AttributeError或根本没有错误。

答案 7 :(得分:0)

对于一般含义,您可以考虑阅读Idioms and Anti-Idioms in Python: Exceptions

在您的特定情况下,正如其他人所述,您应该使用dict.get()

  

get(key [,default])

     

如果键在,则返回key的值   字典,否则默认。如果未给出default,则默认为   无,因此此方法永远不会引发KeyError。