问题:更好地“尝试”某些东西并捕获异常或测试是否有可能首先避免异常?
我应该测试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
Should I test if
something is valid or just try
to do it and catch the exception?
- Is there any solid documentation saying that one way is preferred?
- Is one way more pythonic?
For example, should I:
if len(my_list) >= 4:
x = my_list[3]
else:
x = 'NO_ABC'
Or:
try:
x = my_list[3]
except IndexError:
x = 'NO_ABC'
Some thoughts…
PEP 20 says:
Errors should never pass silently.
Unless explicitly silenced.
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?
I’m not referring to situations where you can only do things 1 way; for example:
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
You should prefer try/except
over if/else
if that results in
- speed-ups (for example by preventing extra lookups)
- cleaner code (fewer lines/easier to read)
Often, these go hand-in-hand.
speed-ups
In the case of trying to find an element in a long list by:
try:
x = my_list[index]
except IndexError:
x = 'NO_ABC'
the try, except is the best option when the index
is probably in the list and the IndexError is usually not raised. This way you avoid the need for an extra lookup by if index < len(my_list)
.
Python encourages the use of exceptions, which you handle is a phrase from Dive Into Python. Your example not only handles the exception (gracefully), rather than letting it silently pass, also the exception occurs only in the exceptional case of index not being found (hence the word exception!).
cleaner code
The official Python Documentation mentions EAFP: Easier to ask for forgiveness than permission and Rob Knight notes that catching errors rather than avoiding them, can result in cleaner, easier to read code. His example says it like this:
Worse (LBYL ‘look before you leap’):
#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)
Better (EAFP: Easier to ask for forgiveness than permission):
try:
return int(s)
except (TypeError, ValueError, OverflowError): #int conversion failed
return None
回答 1
在这种情况下,您应该完全使用其他方法:
x = myDict.get("ABC", "NO_ABC")
不过,通常来说:如果您希望测试经常失败,请使用if
。如果测试相对于尝试操作并失败则捕获异常而言代价高昂,请使用try
。如果以上条件均不适用,则更容易阅读。
In this particular case, you should use something else entirely:
x = myDict.get("ABC", "NO_ABC")
In general, though: If you expect the test to fail frequently, use if
. If the test is expensive relative to just trying the operation and catching the exception if it fails, use try
. If neither one of these conditions applies, go with whatever reads easier.
回答 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’目录时,该命令才会退出。
Using try
and except
directly rather than inside an if
guard should always be done if there is any possibility of a race condition. For example, if you want to ensure that a directory exists, do not do this:
import os, sys
if not os.path.isdir('foo'):
try:
os.mkdir('foo')
except OSError, e
print e
sys.exit(1)
If another thread or process creates the directory between isdir
and mkdir
, you’ll exit. Instead, do this:
import os, sys, errno
try:
os.mkdir('foo')
except OSError, e
if e.errno != errno.EEXIST:
print e
sys.exit(1)
That will only exit if the ‘foo’ directory can’t be created.
回答 3
如果在进行某些操作之前先检查一下是否会失败,那么您可能应该赞成这样做。毕竟,构造异常(包括相关的回溯)需要花费时间。
异常应用于:
- 出乎意料的事情,或者…
- 您需要跳到不只一个逻辑层次的事情(例如a
break
不能使您走得太远),或者…
- 您不确切知道该如何提前处理异常的事情,或者…
- 提前检查故障的成本很高(相对于尝试操作而言)
请注意,通常,真正的答案是“都不是”-例如,在第一个示例中,您真正应该做的只是.get()
提供默认值:
x = myDict.get('ABC', 'NO_ABC')
If it’s trivial to check whether something will fail before you do it, you should probably favor that. After all, constructing exceptions (including their associated tracebacks) takes time.
Exceptions should be used for:
- things that are unexpected, or…
- things where you need to jump more than one level of logic (e.g. where a
break
doesn’t get you far enough), or…
- things where you don’t know exactly what is going to be handling the exception ahead of time, or…
- things where checking ahead of time for failure is expensive (relative to just attempting the operation)
Note that oftentimes, the real answer is “neither” – for instance, in your first example, what you really should do is just use .get()
to provide a default:
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元素时是否发生的信息。
As the other posts mention, it depends on the situation. There are a few dangers with using try/except in place of checking the validity of your data in advance, especially when using it on bigger projects.
- The code in the try block may have a chance to wreak all sorts of havoc before the exception is caught – if you proactively check beforehand with an if statement you can avoid this.
- If the code called in your try block raises a common exception type, like TypeError or ValueError, you may not actually catch the same exception you were expecting to catch – it may be something else that raise the same exception class before or after even getting to the line where your exception may be raised.
e.g., suppose you had:
try:
x = my_list[index_list[3]]
except IndexError:
x = 'NO_ABC'
The IndexError says nothing about whether it occurred when trying to get an element of index_list or my_list.
回答 5
应该使用try而不是if来解释为无声传递的错误吗?如果是这样,您是否通过以这种方式使用它来明确使其静音,从而使其正常运行?
使用try
表示可能会通过错误,这与使其静默通过相反。使用except
导致它根本不通过。
try: except:
在if: else:
逻辑更为复杂的情况下,首选使用。简单胜于复杂。复杂胜于复杂;要求宽恕比允许容易。
警告:“错误永远都不能静默传递”,是代码可能引发您所知道的异常,并且您的设计承认存在这种可能性的情况,但您并未以处理异常的方式进行设计。在我看来,明确地消除错误将像pass
在except
块中那样进行,仅应在了解“不做任何事情”确实是特定情况下的正确错误处理的情况下进行操作。(这是我真正需要使用编写良好的代码进行注释的少数几次。)
但是,在您的特定示例中,都不适合:
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?
Using try
is acknowledging that an error may pass, which is the opposite of having it pass silently. Using except
is causing it not to pass at all.
Using try: except:
is preferred in cases where if: else:
logic is more complicated. Simple is better than complex; complex is better than complicated; and it’s easier to ask for forgiveness than permission.
What “errors should never pass silently” is warning about, is the case where code could raise an exception that you know about, and where your design admits the possibility, but you haven’t designed in a way to deal with the exception. Explicitly silencing an error, in my view, would be doing something like pass
in an except
block, which should only be done with an understanding that “doing nothing” really is the correct error handling in the particular situation. (This is one of the few times where I feel like a comment in well-written code is probably really needed.)
However, in your particular example, neither is appropriate:
x = myDict.get('ABC', 'NO_ABC')
The reason everyone is pointing this out – even though you acknowledge your desire to understand in general, and inability to come up with a better example – is that equivalent side-steps actually exist in quite a lot of cases, and looking for them is the first step in solving the problem.
回答 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
或根本不引发一个错误。
Whenever you use try/except
for control flow, ask yourself:
- Is it easy to see when the
try
block succeeds and when it fails?
- Are you aware of all side effects inside the
try
block?
- Are you aware of all cases in which the
try
block throws the exception?
- If the implementation of the
try
block changes, will your control flow still behave as expected?
If the answer to one or more of these questions is ‘no’, there might be a lot of forgiveness to ask for; most likely from your future self.
An example.
I recently saw code in a larger project that looked like this:
try:
y = foo(x)
except ProgrammingError:
y = bar(x)
Talking to the programmer it turned that the intended control flow was:
If x is an integer, do y = foo(x).
If x is a list of integers, do y = bar(x).
This worked because foo
made a database query and the query would be successful if x
was an integer and throw a ProgrammingError
if x
was a list.
Using try/except
is a bad choice here:
- The name of the exception,
ProgrammingError
, does not give away the actual problem (that x
is not an integer), which makes it difficult to see what is going on.
- The
ProgrammingError
is raised during a database call, which wastes time. Things would get truly horrible if it turned out that foo
writes something to the database before it throws an exception, or alters the state of some other system.
- It is unclear if
ProgrammingError
is only raised when x
is a list of integers. Suppose for instance that there is a typo in foo
‘s database query. This might also raise a ProgrammingError
. The consequence is that bar(x)
is now also called when x
is an integer. This might raise cryptic exceptions or produce unforeseeable results.
- The
try/except
block adds a requirement to all future implementations of foo
. Whenever we change foo
, we must now think about how it handles lists and make sure that it throws a ProgrammingError
and not, say, an AttributeError
or no error at all.
回答 7
对于一般含义,您可以考虑阅读Python中的成语和反成语:异常。
在您的特定情况下,如其他人所述,您应该使用dict.get()
:
get(key [,默认])
如果key在字典中,则返回key的值,否则返回默认值。如果未提供default,则默认为None,因此此方法永远不会引发KeyError。
For a general meaning, you may consider reading Idioms and Anti-Idioms in Python: Exceptions.
In your particular case, as others stated, you should use 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.