关于python:是否可以在上下文管理器的__exit __()方法中访问上下文对象(代码块)?

Is it possible to access the context object (code block) inside the __exit__() method of a context manager?

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

1
2
3
4
5
6
7
8
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

它会像这样使用:

1
2
3
4
5
6
x = 0
with again():
    print x
    x += 1
    if x == 1:
         raise Exception('I hate 1')

而预期的产出将是:

1
2
0
1

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

有可能吗?


with块不作为单独的代码对象存在,因此不存在。 看到这个类似的问题。 在这种情况下,提问者试图反向(从代码块内部访问上下文管理器),但正如这个答案所解释的那样,with块不是一个单独的范围,所以它实际上没有任何单独的 状态。

你可以通过一个例子看到这个:

1
2
3
4
5
6
7
8
9
10
11
12
import contextlib
import dis

@contextlib.contextmanager
def silly():
    yield

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

然后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
>>> 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块的代码就在函数的代码对象内部以及其他所有内容中。 它不作为单独的代码对象存在,也不与函数代码的其余部分区分开来。 你不能以任何理智的方式得到它(我的意思是,不破解字节码)。