15

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

def f():
    try:
        return whatever()
    finally:
        pass # How do I get what `whatever()` returned in here?

是否可以在finally子句中获取返回值?

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

4

4 回答 4

16

不,不是——finally子句的内容独立于返回机制;如果您确实想查看返回的值,则必须按照您提到的那样做,并将其显式保存在范围内的某个位置。

于 2012-12-25T21:38:00.413 回答
6

在return语句之后你绝对必须“进入”吗?

如果在声明之前允许更改,那么您只需要。returnsys.settrace()

之后 获取值return

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

CPython中,我还没有找到查看值堆栈顶部的方法。

  • 不允许动态改变 frame.f_lasti
  • 不允许动态改变 frame.f_code
  • 动态改变 frame.f_trace 是允许的,但似乎没有帮助
  • 在返回语句“已执行”后,从 finally 块中设置跟踪函数未捕获实际返回事件
  • with 语句不捕获返回值
  • 我假设调用者忽略 f 的返回值,因此自省或跟踪调用者无济于事
  • 我假设whatever() 有副作用,不能再次调用
  • 调试器,至少我尝试过的那些,没有得到返回值(?);用 Python 编写的调试器使用 sys.settrace 和/或最后一个异常,它们都不包含堆栈上的返回值。

当然,使用C 级扩展一切皆有可能,这里有一个快速演示:ctypes

"""
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
于 2012-12-26T00:28:32.253 回答
1

这是不可能的。如果您考虑巨大的骇人听闻的黑客攻击,例如检查字节码和摆弄框架对象,则可能是这样。我不确定这是否有意义,因为返回值不在本地,而且我认为您不能通过框架对象访问中间值堆栈(这是保存返回值的位置)。

也许您可以使用ctypesplus C API,但这也可能非常不稳定,并且需要非常熟悉finally. 我对它的了解还不够远,无法判断这是多么可行,但不用说,这完全超出了Python代码所能做的范围。

还有一个额外的问题是可能没有返回值(如果whatever()抛出异常)!我想你可以很容易地检测到这种情况,使用sys.exc_info().

于 2012-12-25T21:39:26.133 回答
1
def f():
    InvalidFoo = object()
    foo = InvalidFoo
    try:
        foo = whatever()
        return foo
    finally:
        if foo is not InvalidFoo:
            # look at foo
于 2012-12-26T00:43:59.117 回答