关于python:如果可以先避免异常,最好是try一些东西并捕获异常或测试?

Better to 'try' something and catch the exception or test if it's possible first to avoid an exception?

我应该测试if什么东西是有效的还是仅仅测试try来做它并捕获异常?

  • 是否有可靠的文件表明首选一种方法?
  • 还有一条路比Python多吗?

例如,我应该:

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

或:

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

一些想法…PEP 20表示:

Errors should never pass silently.
Unless explicitly silenced.

使用try而不是if是否应被解释为静默传递的错误?如果是这样的话,你是不是用这种方式让它安静下来,从而使它正常?

我不是指你只能以一种方式做事的情况;例如:

1
2
3
4
try:
    import foo
except ImportError:
    import baz


如果这导致

  • 加速(例如通过防止额外查找)
  • 更清晰的代码(行数更少/更易于阅读)

通常情况下,这些都是同时进行的。

提速

如果试图通过以下方式在长列表中查找元素:

1
2
3
4
try:
    x = my_list[index]
except IndexError:
    x = 'NO_ABC'

尝试,除了当index可能在列表中并且索引错误通常不会出现时是最好的选择。这样,您就不需要使用if index < len(mylist)进行额外的查找。

python鼓励使用异常,您处理的异常是一个短语,从dive into python开始。您的示例不仅处理异常(优雅地),而不是让它安静地传递,而且异常只发生在未找到索引的异常情况下(因此出现了单词exception!).

清洁码

官方的python文档提到eafp:请求宽恕比请求许可更容易,rob knight注意到捕获错误而不是避免错误,会导致代码更干净、更易于阅读。他的例子是这样说的:

更糟(lbyl"三思而后行"):

1
2
3
4
5
6
7
#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:请求宽恕比请求允许更容易):

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


在这种特定的情况下,您应该完全使用其他的东西:

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

不过,一般来说:如果您希望测试经常失败,请使用if。如果测试相对于尝试操作和捕获异常(如果失败)来说代价高昂,那么使用try。如果这两个条件都不适用,那么就使用更容易阅读的内容。


如果有可能出现种族状况,应始终直接使用tryexcept,而不是在if的警卫室内。例如,如果要确保目录存在,请不要这样做:

1
2
3
4
5
6
7
import os, sys
if not os.path.isdir('foo'):
  try:
    os.mkdir('foo')
  except OSError, e
    print e
    sys.exit(1)

如果另一个线程或进程在isdirmkdir之间创建目录,您将退出。相反,请执行以下操作:

1
2
3
4
5
6
7
import os, sys, errno
try:
  os.mkdir('foo')
except OSError, e
  if e.errno != errno.EEXIST:
    print e
    sys.exit(1)

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


如果在你做某件事之前检查它是否会失败是很琐碎的,那么你可能会赞成这一点。毕竟,构造异常(包括其关联的回溯)需要时间。

例外情况应用于:

  • 意外的事情,或者…
  • 你需要跳不止一个逻辑层次的东西(例如,一个break不能让你走得足够远),或者……
  • 你不知道在什么情况下会提前处理异常,或者……
  • 提前检查故障的代价很高(相对于尝试操作而言)
  • 请注意,通常情况下,真正的答案是"两者都不是"—例如,在第一个示例中,您真正应该做的是使用.get()提供默认值:

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


    Should using a try instead of an if be interpreted as an error passing silently? And if so, are you explicitly silencing it by using it in this way, therefore making it OK?

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

    if: else:逻辑比较复杂的情况下,最好使用try: except:。简单胜于复杂;复杂胜于复杂;请求宽恕比许可更容易。

    "错误永远不应该无声地传递"的警告是,代码可能会引发一个您知道的异常,并且您的设计允许这种可能性,但您还没有以某种方式设计来处理该异常。在我看来,在except块中,明确地消除错误是在执行类似于pass的操作,只应理解"什么都不做"实际上是在特定情况下正确处理错误。(这是少数几次我觉得可能真的需要用写得好的代码写评论。)

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

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

    每个人都指出这一点的原因——即使你承认你总的来说有理解的愿望,并且不能想出一个更好的例子——是在很多情况下实际上存在着对等的边步骤,寻找它们是解决问题的第一步。


    正如其他帖子所提到的,这取决于具体情况。使用Try/Except来代替提前检查数据的有效性有一些危险,尤其是在大型项目中使用它时。

    • try块中的代码可能有机会在捕获异常之前造成各种破坏-如果预先使用if语句进行检查,可以避免这种情况。
    • 如果在try块中调用的代码引发了一个常见的异常类型,如typeerror或valueerror,则实际上您可能无法捕获预期捕获的同一个异常-它可能是在到达可能引发异常的行之前或之后引发同一个异常类的其他东西。

    例如,假设您有:

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

    indexError没有说明在尝试获取index_list或my_list的元素时是否发生了这种情况。


    当你使用try/except来控制流量时,问问你自己:

  • try块成功和失败时,是否很容易看到?
  • 你知道try区块内的所有副作用吗?
  • 您知道try块抛出异常的所有情况吗?
  • 如果try块的实现发生更改,您的控制流是否仍按预期运行?
  • 如果这些问题中的一个或多个的答案是"不",可能会有很多宽恕的要求;最有可能是来自你未来的自我。

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

    1
    2
    3
    4
    try:
        y = foo(x)
    except ProgrammingError:
        y = bar(x)

    与编程人员交谈后发现,预期的控制流程是:

    If x is an integer, do y = foo(x)

    and if x is a list of integers, do y = bar(x).

    这是因为foo进行了数据库查询,如果x是整数,查询将成功;如果x是列表,则抛出ProgrammingError

    在这里使用try/except是一个糟糕的选择:

  • 异常的名称ProgrammingError并没有给出实际的问题(x不是整数)。这使得很难弄清楚到底发生了什么。
  • ProgrammingError在数据库调用期间被引发,这不必要地浪费了时间。如果结果是foo在抛出异常之前向数据库写入某些内容,或更改其他系统的状态,那么情况将变得非常糟糕。
  • 目前还不清楚每一个ProgrammingError是否是由x作为一个列表引起的。例如,假设foo的数据库查询中存在拼写错误。这也可能会增加一个ProgrammingError。其结果是,如果x是一个整数,那么bar(x)现在也被称为整数。这可能会引起神秘的例外或产生不可预见的结果。
  • try/except块增加了对foo未来所有实现的需求。每当我们更改foo时,我们现在必须考虑它如何处理列表,并确保它抛出了ProgrammingError,而不是说AttributeError或根本没有错误。

  • 对于一般意义,您可以考虑在python中阅读习语和反习语:例外。

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

    get(key[, default])

    Return the value for key if key is in the
    dictionary, else default. If default is not given, it defaults to
    None, so that this method never raises a KeyError.