【问题标题】:Python: Is it possible to access the return value inside the `finally` clause?Python:是否可以访问“finally”子句中的返回值?
【发布时间】:2012-12-25 21:36:20
【问题描述】:

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

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

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

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

【问题讨论】:

  • 老兄检查我添加到答案 - 通过 ctypes 工作演示

标签: python return-value finally


【解决方案1】:

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

【讨论】:

  • 您对此有多大把握?你熟悉 Python 处理这个问题的内部机制吗?
  • 相当肯定 - 至少没有办法从 Python 中得到它。你也许可以通过 C 来破解一些东西,但是你在 Python 中最接近这个信息的是inspect,你会注意到那里没有返回值信息的列表。
  • @RamRachum:这也适用于 Java 并且是 try/<nocatch>/finally 的经典机制。举一个具体的例子,一个常见的习惯用法是一个方法抛出异常,因为它无法读取流的内容,同时关闭finally块中的流;只有try块成功完成才会返回结果,否则抛出异常。
【解决方案2】:

在return语句之后你一定要“进入”吗?

如果在return 语句之前允许更改,则您只需要sys.settrace()

return:

之后获取值

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

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

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

当然,一切皆有可能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

【讨论】:

【解决方案3】:

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

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

还有一个问题是可能没有返回值(如果whatever() 抛出异常)!不过,我想您可以使用sys.exc_info() 轻松检测到这种情况。

【讨论】:

    【解决方案4】:
    def f():
        InvalidFoo = object()
        foo = InvalidFoo
        try:
            foo = whatever()
            return foo
        finally:
            if foo is not InvalidFoo:
                # look at foo
    

    【讨论】:

      猜你喜欢
      • 2013-04-09
      • 1970-01-01
      • 2020-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-28
      相关资源
      最近更新 更多