【问题标题】:Handle a specific exception (say, ENOENT) separately from others与其他异常分开处理特定异常(例如,ENOENT)
【发布时间】:2018-01-04 22:21:29
【问题描述】:

在 Python 中,特定的 POSIX 错误条件没有其单独的异常类型 — 它们由 OSError 异常对象内的属性区分。

假设我正在执行文件操作(通过 SFTP 删除可能不存在的文件)并且我想忽略 ENOENT,但仍要处理任何其他错误或异常。是否有可能比以下更优雅地做到这一点?

try:
    action()
except OSError as e:
    if e.errno == errno.ENOENT:
        pass
    else:
        sophisticated_error_handling(e)
except e:
    sophisticated_error_handling(e)

我不喜欢这种方法,因为它涉及重复。

注意:没有 X-Y 问题。 “动作”是一个库函数,不能告诉它忽略 ENOENT。

【问题讨论】:

  • 捕获所有错误并检查 if 语句中的类型?不知道具体如何,但伪代码:try: somecode except e: if e.type == OSError and e.errno == errno.ENOENT: pass / else: sophisticated()

标签: python exception exception-handling


【解决方案1】:

与您建议的代码相比,有一种更短、更惯用的方法来实现相同的结果。

您尝试/捕获您预期的错误,然后检查 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,这可能会为您消除一些元血。

【讨论】:

  • 谢谢,我知道如何重新引发异常。我实际上特别想避免双重尝试,因为它看起来很丑。但是,我喜欢元编程,我非常喜欢你的“方法 #2”——谢谢!
【解决方案2】:

接下来按except只输入错误类型!

test = [1, 2, 3, 4, 5, 6, 'aasd']
for  i in range(50):
   try:
      print(test[i])
except IndexError:
   pass

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-09
    相关资源
    最近更新 更多