关于Python:如何在上下文管理器中抑制给定的异常?

How can I suppress a given exception in a Context Manager?

本问题已经有最佳答案,请猛点这里访问。

当尝试忽略在执行上下文管理器期间引发的异常时,我的选项是什么?同样,以下简单的上下文管理器只传播结果,导致它结束执行,除非捕获:

1
2
3
4
5
6
7
8
9
10
class contextMan:    
    def __enter__(self):
        print("Entering Context")

    def __exit__(self, exc_type, exc_value, traceback):
        print("Exiting Context")

if __name__ =="__main__":
    with contextMan():
        raise IndexError

我当然可以用一个try -- except子句来代替它,但这似乎是一种相当繁琐的方法。当然,有更好的选择来解决这个问题。


除了将其包装在try语句中之外,还有两种方法可以用来在上下文管理器中抑制给定的异常:

  • 如果给定的异常应该被抑制,则使用返回Trueif条件。这是基于指定True值抑制异常的文档。

    这将适用于Python 2.xPython 3.x版本。

  • 使用具有给定异常名称的contextlib.suppress作为参数。

    这只适用于版本> 3.3

  • 第一个选项是一个值得注意的选项,因为它可以在大多数的Python上使用,所以可以移植。它可以通过使用所需的异常来初始化上下文管理器,并在__exit__上添加一个if子句,该子句只允许传播给定的异常:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class contextMan:    
        def __init__(self, exception):
            self.exception = exception

        def __enter__(self):
            print("Entering Context")

        def __exit__(self, exc_type, exc_value, traceback):
            print("Exiting Context")
            return isinstance(exc_value, self.exception)

    if __name__ =="__main__":
        with contextMan(IndexError):
            raise IndexError

    这将抑制任何IndexErrorIndexError子类的实例,从而产生:

    1
    2
    Entering Context
    Exiting Context

    这种方法的缺点是,您要为每个实例添加一个额外的属性,并在一个对象中组合两个不同的逻辑任务,而不是将它们分离。

    第二个选项更加健壮、明确和通用。它是专门为这些场景创建的。它也是一个上下文管理器,因此,通常可以在应该抑制特定异常的任何情况下使用。

    呼叫签名的形式如下:

    1
     contextlib.suppress(*exceptions)

    其中*exceptions是一组要抑制的异常。保持原来的上下文管理器不变,我们现在可以创建一个嵌套的上下文管理器,它还抑制特定的异常:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class contextMan:    
        def __enter__(self):
            print("Entering Context")

        def __exit__(self, exc_type, exc_value, traceback):
            print("Exiting Context")

    if __name__ =="__main__":
        with contextlib.suppress("IndexError")
            with contextMan():
                raise IndexError

    也可以写在同一行,但这会导致一个相当冗长的声明。明显的缺点是,每次使用上下文管理器时,它都会引入另一个语句。

    尽管有两种选择,但应该有一种——最好只有一种——显而易见的方法,我相信第二种是显而易见的方法(至少对于最近的版本)。