与您建议的代码相比,有一种更短、更惯用的方法来实现相同的结果。
您尝试/捕获您预期的错误,然后检查 except 子句中错误对象的条件。诀窍是如果错误对象不是您期望的特定子类型,则重新引发相同的错误对象不变。
import errno
try:
action()
except IOError as ioe:
if ioe.errno not in (errno.ENOENT,):
# this re-raises the same error object.
raise
pass # ENOENT. that line is optional, but it makes it look
# explicit and intentional to fall through the exception handler.
重要的是您只使用raise 重新引发初始错误,不带参数。你可能会很想用raise ioe 再次加注,而不仅仅是在那条线上的raise。即使您会保留相同的错误消息,但如果您使用raise ioe,它会使错误的堆栈跟踪看起来好像错误发生在该行上,而不是在真正发生的action() 内。
在您提议的代码中,您的第二个异常处理程序(即except e)即使在语法上有效,也不会触发。您必须指定except <type>: 或except <type> as <variable>:。
如果您想进行额外的复杂错误处理,以确保正确性,您可以将之前的所有 try/catch 嵌套在外部 try/except:
try:
try:
action()
except IOError as ioe:
if ioe.errno not in (errno.ENOENT,): raise
except IOError as any_other_ioerror:
# this is reached in case you get other errno values
sophisticated_error_handling()
except OtherExceptionTypeICareAbout as other:
other_fancy_handler()
在嵌套这样的异常处理程序时,您需要注意的一件事是,异常处理程序也可以引发异常。在文档here 中有一个异常处理的教程,但是你可能会觉得它有点密集。
方法#2
如果您喜欢元 python 技巧,可以考虑使用with statement 的方法。您可以创建一个 Python 上下文管理器来吸收特定类型的错误。
# add this to your utilities
class absorb(object):
def __init__(self, *codes):
self.codes = codes
def __exit__(self, exc_type, value, traceback):
if hasattr(value, "errno") and getattr(value, "errno") in self.codes:
return True # exception is suppressed
return False # exception is re-raised
def __enter__(self):
return None
然后你可以编写简单的代码,例如:
# if ENOENT occurs during the block, abort the block, but do not raise.
with absorb(errno.ENOENT):
delete_my_file_whether_it_exists_or_not()
print("file deleted.") # reachable only if previous call returned
如果您仍然希望对其他 errno 情况进行复杂的错误处理,您可以在 try/except 中包含上述内容,如下所示:
try:
with absorb(errno.ENOENT): action()
# the code here is reachable only if action() returns,
# or if one of the suppressed/absorbed exceptions has been raised (i.e. ENOENT)
except IOError as any_other_ioerror:
# any exception with errno != ENOENT will come here
sophisticated_error_handling()
注意:您可能还想看看contextlib,这可能会为您消除一些元血。