如何抑制上下文管理器中的给定异常?

Jim*_*ard 7 python python-3.x

当尝试忽略上下文管理器执行期间引发的异常时,我有哪些选择?事实上,以下简单的上下文管理器只是传播结果,导致其结束执行,除非被捕获:

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
Run Code Online (Sandbox Code Playgroud)

我当然可以将它放在一个try -- except子句中,但这似乎是一种相当乏味的方法。当然,有更好的选择来解决这个问题。

Jim*_*ard 9

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

  1. 使用if返回True是否应抑制给定异常的条件。这是基于指定值抑制异常的文档。True

    Python 2.x这将在不同版本中起作用Python 3.x

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

    这只适用于版本> 3.3


第一个选项是一个值得注意的选项,因为它可以在大多数 Python 上运行,因此可移植性很重要。只需使用所需的异常初始化上下文管理器,然后__exit__添加一个if仅允许传播给定异常的子句即可完成此操作:

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
Run Code Online (Sandbox Code Playgroud)

现在,这将抑制 的任何实例IndexError或子类的任何实例,IndexError从而导致输出:

Entering Context
Exiting Context
Run Code Online (Sandbox Code Playgroud)

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


第二种选择更加稳健明确通用。它是专门为此类场景创建的。它也是一个上下文管理器,因此通常可用于应抑制特定异常的任何情况。

它的调用签名的形式如下:

 contextlib.suppress(*exceptions) 
Run Code Online (Sandbox Code Playgroud)

哪里*exceptions有要抑制的异常元组。保持原始上下文管理器不变,我们现在可以创建一个嵌套上下文管理器,它也可以抑制特定的异常:

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
Run Code Online (Sandbox Code Playgroud)

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

尽管有两种选择,但应该有一种(最好只有一种)明显的方法来做到这一点,我相信第二种方法是明显的方法(至少对于最近的版本)。