最好是“尝试”一些事情,然后捕获异常或测试,如果可能的话,首先避免一个异常?

我应该测试 if什么是有效的或只是 try这样做,并捕获异常?

  • 是否有任何可靠的文件说明一种方法是首选的?
  • 有没有一种方式更 蟒蛇

例如:

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
48246 次浏览

如果在做某件事之前检查它是否会失败是微不足道的,那么您可能应该赞成这样做。毕竟,构造异常(包括它们相关的回溯)需要时间。

例外情况应用于:

  1. 意想不到的事情,或者..。
  2. 你需要跳过不止一个逻辑层次的东西(例如,break不能带你到足够远的地方) ,或者..。
  3. 不知道提前处理异常的具体方法,或者..。
  4. 事先检查是否失败是昂贵的(相对于仅仅尝试操作)

注意,通常情况下,真正的答案是“两者都不是”——例如,在第一个示例中,实际上 应该所做的只是使用 .get()提供一个默认值:

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

在这种特殊情况下,您应该完全使用其他内容:

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

但是,一般来说: 如果您预计测试会经常失败,那么使用 if。如果相对于仅仅尝试操作并在操作失败时捕获异常而言,测试代价较高,则使用 try。如果这两个条件都不适用,那么使用任何更容易阅读的条件。

是否应该将使用 try 而不是 if 解释为无声传递的错误?如果是这样,你是否通过这种方式明确地使它沉默,从而使它成为可能?

使用 try是承认一个错误可能会通过,这与让它静默地通过是相反的。使用 except导致它根本无法通过。

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

“错误永远不应该悄无声息地传递”的警告是,代码可能引发您已经知道的异常,您的设计承认这种可能性,但是您没有设计处理异常的方式。在我看来,显式地消除错误可能是在 except块中执行类似于 pass的操作,只有在理解“什么都不做”实际上是特定情况下正确的错误处理的情况下才能执行这种操作。(这是为数不多的几次我觉得需要在编写良好的代码中添加注释的情况之一。)

然而,在你的例子中,两者都不合适:

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

每个人都指出这一点的原因是——即使你承认自己总体上渴望理解,却无法想出一个更好的例子——实际上在很多情况下都存在类似的边缘步骤,寻找它们是解决问题的第一步。

如果结果是 try/except,那么您应该选择 try/except而不是 if/else

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

通常情况下,这两者是相辅相成的。


加速

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

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

index可能在列表中且通常不会引发 IndexError 时,try (除外)是最佳选项。通过这种方式,您可以避免使用 if index < len(my_list)进行额外的查找。

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

更好的 (EAFP: 请求原谅比请求许可更容易):

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

对于一般意义,你可以考虑阅读 《 Python 》中的习语与反习语: 例外

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

Get (key [ ,default ])

属性中的键返回键的值 字典,否则默认。如果没有给出默认值,则默认为 无,因此此方法永远不会引发 KeyError。

正如其他帖子提到的,这取决于具体情况。使用 try/but 代替事先检查数据的有效性有一些危险,特别是在大型项目中使用它时。

  • Try 块中的代码可能有机会在捕获异常之前造成各种破坏——如果您事先使用 if 语句进行检查,就可以避免这种情况。
  • 如果在 try 块中调用的代码引发了一个常见的异常类型,比如 TypeError 或 ValueError,那么您可能实际上并没有捕获到您预期捕获的异常——它可能是在到达可能引发异常的行之前或之后引发了相同的异常类。

例如,假设你有:

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

IndexError 没有说明在尝试获取 index _ list 或 my _ list 元素时是否发生了此错误。

使用 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”目录时才会退出。

无论何时使用 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是一个整数,那么查询将成功,如果 x是一个列表,则抛出一个 ProgrammingError

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

  1. 异常的名称 ProgrammingError并没有暴露实际的问题(即 x不是整数) ,这使得很难看出发生了什么。
  2. ProgrammingError在数据库调用期间引发,这会浪费时间。如果事实证明 foo在抛出异常之前向数据库写入了一些内容,或者改变了其他系统的状态,那么情况将变得非常糟糕。
  3. 目前还不清楚 ProgrammingError是否只在 x是一个整数列表时引发。例如,假设在 foo的数据库查询中有一个输入错误。这也可能提高 ProgrammingError。结果是,当 x是一个整数时,bar(x)也被调用。这可能会引发神秘的异常或产生不可预见的结果。
  4. try/except块为 foo的所有未来实现添加了一个需求。无论何时更改 foo,我们现在都必须考虑它如何处理列表,并确保它抛出的是 ProgrammingError,而不是 AttributeError,或者根本没有错误。