问题:更好地“尝试”某些东西并捕获异常或测试是否有可能首先避免异常?
我应该测试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说:
错误绝不能默默传递。
除非明确地保持沉默。
应该使用a try
而不是an if
解释为无声传递的错误吗?如果是这样,您是否通过以这种方式使用它来明确使其静音,从而使其正常运行?
我不是指只能以一种方式做事的情况;例如:
try:
import foo
except ImportError:
import baz
回答 0
你应该更喜欢try/except
过if/else
如果结果
- 加快速度(例如,通过防止额外的查询)
- 更清晰的代码(行数更少/更易于阅读)
通常,它们并存。
加速
如果尝试通过以下方式在长列表中查找元素:
try:
x = my_list[index]
except IndexError:
x = 'NO_ABC'
当index
可能在列表中并且通常不引发IndexError 时,尝试除外是最好的选择。这样,您就可以避免进行额外的查询if index < len(my_list)
。
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(s)
更好(EAFP:寻求宽恕比获得许可更容易):
try:
return int(s)
except (TypeError, ValueError, OverflowError): #int conversion failed
return None
回答 1
在这种情况下,您应该完全使用其他方法:
x = myDict.get("ABC", "NO_ABC")
不过,通常来说:如果您希望测试经常失败,请使用if
。如果测试相对于尝试操作并失败则捕获异常而言代价高昂,请使用try
。如果以上条件均不适用,则更容易阅读。
回答 2
使用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’目录时,该命令才会退出。
回答 3
如果在进行某些操作之前先检查一下是否会失败,那么您可能应该赞成这样做。毕竟,构造异常(包括相关的回溯)需要花费时间。
异常应用于:
- 出乎意料的事情,或者…
- 您需要跳到不只一个逻辑层次的事情(例如a
break
不能使您走得太远),或者… - 您不确切知道该如何提前处理异常的事情,或者…
- 提前检查故障的成本很高(相对于尝试操作而言)
请注意,通常,真正的答案是“都不是”-例如,在第一个示例中,您真正应该做的只是.get()
提供默认值:
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元素时是否发生的信息。
回答 5
应该使用try而不是if来解释为无声传递的错误吗?如果是这样,您是否通过以这种方式使用它来明确使其静音,从而使其正常运行?
使用try
表示可能会通过错误,这与使其静默通过相反。使用except
导致它根本不通过。
try: except:
在if: else:
逻辑更为复杂的情况下,首选使用。简单胜于复杂。复杂胜于复杂;要求宽恕比允许容易。
警告:“错误永远都不能静默传递”,是代码可能引发您所知道的异常,并且您的设计承认存在这种可能性的情况,但您并未以处理异常的方式进行设计。在我看来,明确地消除错误将像pass
在except
块中那样进行,仅应在了解“不做任何事情”确实是特定情况下的正确错误处理的情况下进行操作。(这是我真正需要使用编写良好的代码进行注释的少数几次。)
但是,在您的特定示例中,都不适合:
x = myDict.get('ABC', 'NO_ABC')
每个人都指出这一点的原因-即使您承认您希望总体上理解并且无法提出更好的例子-是在很多情况下实际上存在等效的避让,而寻找它们是解决问题的第一步。
回答 6
每当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
则将抛出if x
。
try/except
在这里使用是一个不好的选择:
- 异常的名称
ProgrammingError
不会给出实际的问题(x
不是整数),这使得很难看到发生了什么。 - 该
ProgrammingError
数据库调用,浪费时间内上升。如果事实证明是foo
在引发异常之前将某些内容写入数据库或更改了其他系统的状态,那么事情将变得非常可怕。 - 尚不清楚是否
ProgrammingError
仅在x
整数列表时才引发。例如,假设foo
的数据库查询中有错字。这可能还会引发一个ProgrammingError
。结果是,bar(x)
当x
是整数时,现在也称为。这可能会引发神秘异常或产生不可预见的结果。 - 该
try/except
块为的所有未来实现增加了要求foo
。每当我们进行更改时foo
,我们现在都必须考虑它如何处理列表,并确保它引发一个错误,ProgrammingError
而不是一个AttributeError
或根本不引发一个错误。
回答 7
对于一般含义,您可以考虑阅读Python中的成语和反成语:异常。
在您的特定情况下,如其他人所述,您应该使用dict.get()
:
get(key [,默认])
如果key在字典中,则返回key的值,否则返回默认值。如果未提供default,则默认为None,因此此方法永远不会引发KeyError。