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

问题描述 投票:121回答:8

我应该测试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
python exception-handling if-statement try-catch pep
8个回答
140
投票

你应该更喜欢try/except而不是if/else

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

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


速度起坐

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

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

尝试,除了是index可能在列表中并且通常不会引发IndexError时的最佳选项。这样您就可以避免if index < len(mylist)需要额外查找。

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


清洁代码

官方的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

17
投票

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

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

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


8
投票

如果有任何竞争条件的可能性,应该始终使用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'目录时才会退出。


7
投票

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

例外情况应该用于:

  1. 意想不到的事情,或......
  2. 你需要跳过多个逻辑级别的东西(例如,break不能让你走得太远),或者......
  3. 你不确切知道将要提前处理异常的事情,或者......
  4. 提前检查故障的事情是昂贵的(相对于只是尝试操作)

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

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

4
投票

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

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

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

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

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

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

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


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元素时是否发生了这种情况。


2
投票

每当您使用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或根本没有错误。

0
投票

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

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

get(key [,default])

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

© www.soinside.com 2019 - 2024. All rights reserved.