【问题标题】:Is it possible to access the context object (code block) inside the __exit__() method of a context manager?是否可以访问上下文管理器的 __exit__() 方法中的上下文对象(代码块)?
【发布时间】:2014-01-21 02:38:23
【问题描述】:

如果代码对象引发异常(可能多次,可能有延迟),我想在 exit() 方法中再次调用代码对象。我知道使用装饰器很容易做到,但我的动机是有时我只想重复一些我不想提取到单独函数并装饰它的代码片段。我正在寻找类似的东西:

class again(object):
    def __enter__(self):
        pass

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is not None:
            ????        # Invoke the code object again
            return True # eat exception

它会像这样使用:

x = 0
with again():
    print x
    x += 1
    if x == 1:
         raise Exception('I hate 1')

预期的输出是:

0
1  

我可以找到一种方法来获取代码对象。上下文管理器属性似乎都没有引用它(我猜它并不是真正需要的,因为它的工作只是在前后做一些事情)。

有可能吗?

【问题讨论】:

  • “代码对象”是什么意思?
  • 我非常怀疑这是可能的——主要是因为这不是上下文管理器的用途。正如您所提到的,python 处理此问题的方式是通过函数(可能使用装饰器)。你所描述的对我来说更像红宝石......
  • 上下文管理器用于管理资源。它们从未被设计为“块装饰器”来对代码块的功能进行任意更改,这正是您所寻找的。​​span>

标签: python contextmanager


【解决方案1】:

with 块不作为单独的代码对象存在,所以不存在。见this similar question。在那种情况下,提问者试图做相反的事情(从代码块内部访问上下文管理器),但正如this answer 解释的那样,with 块不是一个单独的范围,所以它实际上没有任何单独的状态。

你可以用一个例子来看看:

import contextlib
import dis

@contextlib.contextmanager
def silly():
    yield

def foo():
    print "Hello"
    with silly():
        print "Inside"
    print "Goodbye"

然后

>>> dis.dis(foo.__code__)
  2           0 LOAD_CONST               1 (u'Hello')
              3 PRINT_ITEM          
              4 PRINT_NEWLINE       

  3           5 LOAD_GLOBAL              0 (silly)
              8 CALL_FUNCTION            0
             11 SETUP_WITH              10 (to 24)
             14 POP_TOP             

  4          15 LOAD_CONST               2 (u'Inside')
             18 PRINT_ITEM          
             19 PRINT_NEWLINE       
             20 POP_BLOCK           
             21 LOAD_CONST               0 (None)
        >>   24 WITH_CLEANUP        
             25 END_FINALLY         

  5          26 LOAD_CONST               3 (u'Goodbye')
             29 PRINT_ITEM          
             30 PRINT_NEWLINE       
             31 LOAD_CONST               0 (None)
             34 RETURN_VALUE  

您可以看到with 块的代码与其他所有内容都位于函数的代码对象中。它不作为单独的代码对象存在,也不与函数的其余代码区分开来。你不能以任何理智的方式把它弄出来(我的意思是,不破解字节码)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-04-20
    • 2020-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多