关于python:如何操作上下文管理器的__exit__中的异常?

How to manipulate the exception in __exit__ of a context manager?

我知道从上下文管理器的__exit__()方法中重新引发异常是一种糟糕的风格。所以,我想在实例上附加一个属性,它可以携带上下文信息,如果我让异常通过或者捕获它,这些信息是不可用的。这将避免重新提升它。

将属性附加到异常上的另一种方法是吞咽异常,在作为相关上下文管理器的实例上设置一些状态,然后检查该状态。问题是这会导致第22条军规,不是吗?因为异常意味着在with块内的执行被退出。除了再次进入with块外,无法重复该操作,对吗?因此,当__exit__()方法返回时,我试图存储上下文信息的实例将消失。

简而言之:当使用__exit__()方法时,如何处理待处理的实际异常(如果是,我将假定为针对这个问题给出的异常)?


上下文管理器不会因为块退出而消失。您可以通过两种方式保存它:

  • 首先创建上下文管理器,将其分配给变量,然后将with与该对象一起使用:

    1
    2
    3
    4
    5
    cm = ContextManager()
    with cm:
        # ....

    state = cm.attribute
  • __enter__方法返回上下文管理器本身,使用with ... as ...将其绑定到本地名称。当with退出时,该名称不会解除绑定:

    1
    2
    3
    4
    with ContextManager as cm:
        # ....

    state = cm.attribute

    其中ContextManager.__enter__使用return self

  • 您还可以对异常本身设置额外的属性;无需重新引发异常:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    >>> class ContextManager(object):
    ...     def __enter__(self):
    ...         return self
    ...     def __exit__(self, tp, v, tb):
    ...         if tp is None: return
    ...         v.extra_attribute = 'foobar'
    ...         self.other_extra_attribute = 'spam-n-ham'
    ...
    >>> try:
    ...     with ContextManager() as cm:
    ...         raise ValueError('barfoo')
    ... except ValueError as ex:
    ...     print vars(ex)
    ...
    {'extra_attribute': 'foobar'}
    >>> vars(cm)
    {'other_extra_attribute': 'spam-n-ham'}

    在这里,异常被赋予了一个额外的属性,该属性一直持续到异常处理程序。在上面,我还表明cm仍然与上下文管理器绑定。