基本上有三种方法可以使用with语句:
使用现有的上下文管理器:
with manager:
pass
创建上下文管理器并将其结果绑定到变量:
^{pr2}$创建上下文管理器并放弃其返回值:
with Manager():
pass
如果我们把一个函数get_manager()
放在上面的三个块中,有没有实现可以返回封闭的上下文管理器,或者至少返回它们的__exit__
函数?在
第一种情况显然很简单,但我想不出在另外两种情况下能起作用的方法。我怀疑是否有可能获得整个上下文管理器,因为值堆栈是在SETUP_WITH
操作码之后立即弹出的。但是,既然__exit__
函数是由^{
不幸的是,正如评论中所讨论的,这并非在所有情况下都可能。创建上下文管理器时,following code is run(至少在cpython2.7中是这样的)。我无法评论其他实现):
使用
^{pr2}$__exit__
宏将SET_TOP
方法推送到堆栈上,该宏是defined as:堆栈指针依次设置在frame eval开头的帧值堆栈的顶部:
其中f是在frameobject.h中定义的帧对象。对我们来说不幸的是,这是线索停止的地方。python可访问框架对象是用following methods only定义的:
不幸的是,它没有包括我们需要的
f_valuestack
。这是有意义的,因为f_valuestack
是PyObject **
类型,它需要包装在一个对象中,以便从python以任何方式访问。在TL;DR:我们要寻找的
__exit__
方法只位于一个地方,即frame对象的值堆栈,而cPython不允许python代码访问值堆栈。在这种情况与类似的出现案例(如
super
)之间的区别在于,这里没有可查看的封闭框架。with
语句不是新范围。sys._getframe(0)
(或者,如果您将代码放入函数中,sys._getframe(1)
)可以正常工作,但它将返回与with
语句前后相同的帧。在唯一的方法就是检查字节码。但即使这样也无济于事。例如,请尝试以下操作:
显然,正如^{} 所解释的那样,该方法确实会被查找并推送到堆栈上,供^{} 以后使用。因此,即使
POP_TOP
删除了silly()
的返回值,它的__exit__
仍然在堆栈中。在从Python那里走不到那里。除非您想开始咀嚼字节码,或者用
ctypes
或其他东西来挖掘堆栈,否则它可能不存在。在如果上下文管理器是一个类,并且只有一个实例,那么您可以在堆上找到它:
(免责声明:不要这样做)
相关问题 更多 >
编程相关推荐