Python:是否可以访问'finally'子句中的返回值?

Python:是否可以访问'finally'子句中的返回值?,python,return-value,finally,Python,Return Value,Finally,我在try子句中有一个return语句: def f(): try: return whatever() finally: pass # How do I get what `whatever()` returned in here? 是否可以在finally子句中获取返回值 这更多的是一个理论问题,所以我不是在寻找一个像将其保存到变量这样的解决方法。不,它不是-最后子句的内容独立于返回机制;如果您确实想查看返回的值,您必须按照前面提到的操作,并

我在
try
子句中有一个return语句:

def f():
    try:
        return whatever()
    finally:
        pass # How do I get what `whatever()` returned in here?
是否可以在
finally
子句中获取返回值


这更多的是一个理论问题,所以我不是在寻找一个像将其保存到变量这样的解决方法。

不,它不是-
最后
子句的内容独立于返回机制;如果您确实想查看返回的值,您必须按照前面提到的操作,并将其显式保存在范围内的某个位置。

这是不可能的。如果你考虑巨型骇客黑客,比如检查字节码和摆弄帧对象,可能会是这样。我不确定这一点是否有意义,因为返回值不在本地,而且我认为您无法通过帧对象访问中间值堆栈(保存返回值的地方)

也许您可以使用
ctypes
加上C API,但这可能也很不稳定,需要非常熟悉
的实现。我对它的了解还远远不够,无法判断它的可行性,但不用说,这完全超出了Python代码的功能范围

另外还有一个问题,就是可能没有返回值(如果
where()
抛出异常)!不过,我想您可以通过使用
sys.exc_info()

很容易检测到这种情况,您是否一定要在return语句之后“进入”呢

语句之前允许的更改,sys.settrace()是您所需要的全部

只有在返回后:

我认为,在无堆栈Python中,您应该能够做到这一点。“线程”可以在stackless中进行酸洗,即将返回的值,也就是值堆栈的顶部,应该在那个里

在CPython中,我还找不到一种方法可以窥视价值堆栈的顶部

  • 不允许动态更改frame.f_lasti
  • 不允许动态更改frame.f_代码
  • 允许动态更改frame.f_跟踪,但似乎没有帮助
  • 在return语句“已执行”后,来自最终块的set跟踪函数未捕获实际返回事件
  • with station不捕捉返回值
  • 我假设调用者忽略了f的返回值,因此内省或跟踪调用者没有帮助
  • 我假设which()有副作用,不能再次调用
  • 调试器,至少我尝试过的调试器,没有得到返回值(?);用Python编写的调试器使用sys.settrace和/或last exception,它们都不包含堆栈上的返回值
当然,使用ctypes或c级扩展,一切都是可能的,下面是一个快速演示:

"""
valuestack.py: Demo reading values from Python value stack
Offsets are derived for CPython-2.7.2, 64-bit, production build
"""
import ctypes, inspect, random

def id2obj(i):
    """convert CPython `id` back to object ref, by temporary pointer swap"""
    tmp = None,
    try:
        ctypes.cast(id(tmp), ctypes.POINTER(ctypes.c_ulong))[3] = i
        return tmp[0]
    finally:
        ctypes.cast(id(tmp), ctypes.POINTER(ctypes.c_ulong))[3] = id(None)

def introspect():
    """pointer on top of value stack is id of the object about to be returned
    FIXME adjust for sum(vars, locals) in introspected function
    """
    fr = inspect.stack()[1][0]
    print "caught", id2obj(ctypes.cast(id(fr), ctypes.POINTER(ctypes.c_ulong))[47])

def value():
    tmp = random.random()
    print "return", tmp
    return tmp

def foo():
    try:
        return value()
    finally:
        introspect()

if __name__ == "__main__":
    foo()
与osx附带的64位模式下的Python-2.7.2配合使用:

air:~ dima$ python valuestack.py 
return 0.959725159294
caught 0.959725159294

你对此有多确定?你熟悉Python如何处理这个问题的内部机制吗?非常肯定——至少从Python是无法理解的。您可能可以通过C来处理这些东西,但在Python中最接近这些信息的是,您会注意到其中没有返回值信息的列表。@RamRachum:这对于Java也是正确的,是
try//finally
的经典机制。举一个具体的例子,一个常见的习惯用法是方法抛出异常,因为它无法读取流的内容,同时在
finally
块中关闭流;只有当
try
块成功完成时,才会返回结果,否则会引发异常。感谢您的研究!对于Python stacksdude的一个相当彻底的回顾,请检查我的add to answer——通过ctypes工作的演示
def f():
    InvalidFoo = object()
    foo = InvalidFoo
    try:
        foo = whatever()
        return foo
    finally:
        if foo is not InvalidFoo:
            # look at foo