我应该测试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
你应该更喜欢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
在这种特殊情况下,您应该完全使用其他东西:
x = myDict.get("ABC", "NO_ABC")
但是,一般情况下:如果您希望测试频繁失败,请使用if
。如果测试相对于仅尝试操作而言是昂贵的,并且如果失败则捕获异常,请使用try
。如果这些条件都不适用,请更容易阅读。
如果有任何竞争条件的可能性,应该始终使用try
和except
直接而不是在if
后卫内部。例如,如果要确保存在目录,请不要这样做:
import os, sys
if not os.path.isdir('foo'):
try:
os.mkdir('foo')
except OSError, e
print e
sys.exit(1)
如果另一个线程或进程在isdir
和mkdir
之间创建目录,您将退出。相反,这样做:
import os, sys, errno
try:
os.mkdir('foo')
except OSError, e
if e.errno != errno.EEXIST:
print e
sys.exit(1)
只有在无法创建'foo'目录时才会退出。
如果在你做之前检查某些东西是否会失败是微不足道的,你应该赞成这一点。毕竟,构造异常(包括其相关的追溯)需要时间。
例外情况应该用于:
break
不能让你走得太远),或者......请注意,通常情况下,真正的答案是“不” - 例如,在您的第一个示例中,您真正应该做的只是使用.get()
来提供默认值:
x = myDict.get('ABC', 'NO_ABC')
应该使用try而不是if被解释为默认传递的错误?如果是这样,你是否通过这种方式使用它来明确地对它进行静音,从而使它成为可能?
使用try
承认错误可能会通过,这与它默默通过相反。使用except
导致它根本不通过。
在try: except:
逻辑更复杂的情况下,首选使用if: else:
。简单比复杂更好;复杂比复杂更好;要求宽恕比获得许可更容易。
什么“错误永远不会无声地传递”是警告,代码可以引发您知道的异常,以及您的设计承认可能性,但您没有设计处理异常的方式。在我看来,明确地消除错误会在pass
块中执行类似except
的操作,这应该只是理解“无所事事”在特定情况下才是正确的错误处理。 (这是我觉得可能真的需要在编写良好的代码中发表评论的少数几次之一。)
但是,在您的特定示例中,两者都不合适:
x = myDict.get('ABC', 'NO_ABC')
每个人都指出这一点的原因 - 即使你承认你一般都需要理解,并且无法提出一个更好的例子 - 是在很多情况下实际存在相同的副步骤,并且寻找它们是解决问题的第一步。
正如其他帖子所说,这取决于具体情况。使用try / except会有一些危险,而不是提前检查数据的有效性,尤其是在大型项目中使用它时。
例如,假设你有:
try:
x = my_list[index_list[3]]
except IndexError:
x = 'NO_ABC'
IndexError没有说明在尝试获取index_list或my_list元素时是否发生了这种情况。
每当您使用try/except
进行控制流程时,请问自己:
try
阻止成功何时失败?try
区块内的所有副作用吗?try
阻止抛出异常的所有情况?try
块的实现发生变化,您的控制流程是否仍会按预期运行?如果对这些问题中的一个或多个问题的答案为“否”,则可能会有很多宽恕要求;最有可能来自你未来的自我。
一个例子。我最近在一个看起来像这样的大项目中看到了代码:
try:
y = foo(x)
except ProgrammingError:
y = bar(x)
与程序员交谈后,它转向了预期的控制流程:
如果x是整数,则执行y = foo(x)。
如果x是整数列表,请执行y = bar(x)。
这是因为foo
进行了数据库查询,如果x
是一个整数,查询将成功,如果ProgrammingError
是一个列表,则抛出x
。
在这里使用try/except
是一个糟糕的选择:
ProgrammingError
例外的名称并没有泄露实际问题(x
不是整数),这使得很难看出发生了什么。ProgrammingError
是在数据库调用期间引发的,这会浪费时间。如果事实证明foo
在抛出异常之前向数据库写了一些内容,或者改变了其他系统的状态,事情就会变得非常可怕。ProgrammingError
是否仅在x
是整数列表时才会被提出。例如,假设foo
的数据库查询中存在拼写错误。这也可能会引发ProgrammingError
。结果是当bar(x)
是一个整数时,x
现在也被调用。这可能会引发神秘异常或产生不可预见的结果。try/except
块增加了对foo
的所有未来实现的要求。每当我们改变foo
时,我们现在必须考虑它如何处理列表并确保它抛出ProgrammingError
而不是,例如,AttributeError
或根本没有错误。对于一般意义,您可以考虑阅读Idioms and Anti-Idioms in Python: Exceptions。
在您的特定情况下,正如其他人所述,您应该使用dict.get()
:
get(key [,default])
如果key在字典中,则返回key的值,否则返回default。如果未给出default,则默认为None,因此此方法永远不会引发KeyError。