【问题标题】:How to manipulate the exception in __exit__ of a context manager?如何在上下文管理器的 __exit__ 中操作异常?
【发布时间】:2014-11-13 10:53:03
【问题描述】:

我知道从上下文管理器的 __exit__() 方法中重新引发异常是一种不好的风格。因此,我想在实例上添加一个属性,该属性可以携带如果我让异常通过或捕获它时不可用的上下文信息。这样可以避免重新加注。

在异常上附加属性的替代方法是吞下异常,在实例上设置一些状态,该实例兼作相关上下文管理器,然后检查该状态。问题是这会导致捕获 22,不是吗?由于异常意味着正在退出 with 块内的执行。除了再次输入with块之外,没有其他方法可以重复操作,对吧?因此,一旦__exit__() 方法返回,我尝试存储上下文信息的实例就会消失。

简而言之:在 __exit__() 方法中,我如何操纵实际未决的异常(如果是,我将假定为这个问题)?

【问题讨论】:

  • 谁说风格不好?上下文管理器是转换异常的绝佳方式。
  • @MartijnPieters:实际上 文档 指出这是一种不好的风格,因为它会产生嵌套上下文管理器的问题。是的,对于转换和不嵌套,很好。
  • 您不应该重新引发相同的异常(只返回None 可确保保持异常状态),但您可以引发new 异常。
  • @MartijnPieters:确实如此。但例外是好的,除了我需要在调用链上携带一些信息。所以这就是为什么在我的情况下再加注是不好的风格。

标签: python python-2.x contextmanager


【解决方案1】:

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

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

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

    with ContextManager() as cm:
        # ....
    
    state = cm.attribute
    

    其中ContextManager.__enter__ 使用return self

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

>>> 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 仍然绑定到上下文管理器。

【讨论】:

  • 我的假设是您的v.extra_attribute = 'foobar' 和我的setattr(exc_value, 'extra_attribute', 'foobar') 在功能上是等效的。但是,使用它会得到AttributeError: 'str' object has no attribute 'extra_attribute'。为什么我看到的值是 str 实例而不是异常实例?它在__exit__() 方法中。
  • 您是否引发了 string 异常?这是非常过时的。仅使用对象异常(BaseException 的子类)。
  • 如果您无法控制引发哪些异常,则无法为那些旧式字符串异常设置属性。标准库当然不会再提出这些了,但我不知道你在这里使用的是什么系统。 :-)
  • 最后但并非最不重要的一点是,字符串异常支持在 Python 2.6 中已完全删除,因此如果您在 2.6 或 2.7 上看到这一点,则可能是其他错误。你能给我看一个要点或馅饼来证明你所看到的问题吗?我当然无法在 Python 2.7 中重现 setattr() 的任何问题。
  • @0xC0000022L: 发现错误;这是 Python 2.6 实现的一个问题:bugs.python.org/issue7853
猜你喜欢
  • 1970-01-01
  • 2021-10-15
  • 2016-05-30
  • 2020-06-07
  • 2015-03-25
  • 1970-01-01
  • 1970-01-01
  • 2023-03-26
相关资源
最近更新 更多