【问题标题】:Getting callable object from the frame从框架中获取可调用对象
【发布时间】:2010-11-11 01:52:57
【问题描述】:

给定框架对象(例如由sys._getframe 返回),我可以得到底层的可调用对象吗?

代码说明:

def foo():
    frame = sys._getframe()
    x = some_magic(frame)

    # x is foo, now

请注意,我的问题是将对象从框架中取出,而不是当前调用的对象。

希望这是可能的。

干杯,

MH

编辑:

我已经设法解决了这个问题。它深受 Andreas 和 Alexander 回复的启发。感谢大家投入的时间!

def magic():
    fr = sys._getframe(1)
    for o in gc.get_objects():
        if inspect.isfunction(o) and o.func_code is fr.f_code:
            return o 

class Foo(object):
    def bar(self):
        return magic()

x = Foo().bar()

assert x is Foo.bar.im_func

(适用于 2.6.2,对于 py3k,将 func_code 替换为 __code__,将 im_func 替换为 __func__)

然后,我可以积极地遍历 globals() 或 gc.get_objects() 和 dir() 一切以寻找具有给定函数对象的可调用对象。

对我来说感觉有点不合时宜,但很有效。

再次感谢!

MH

【问题讨论】:

  • 正如您在下面的答案中看到的那样,这是可以做到的。虽然不推荐。你想达到什么目的?很可能可以通过其他方式完成。
  • 我正在尝试做的实际事情是记录程序中的所有函数调用(并稍后处理那些被调用的函数)。为此,我正在使用 bdb 模块跟踪所有堆栈帧 - 它为我提供了帧对象。我会非常感谢任何想法!
  • 按照流程,您是说要给他们打电话还是在做其他事情?
  • 看看这个:stackoverflow.com/questions/541329/…我不羡慕你...
  • @MikeHordecki,您可以使用gc.get_referrers(fr.f_code) 代替gc.get_objects()。

标签: python


【解决方案1】:

不是真正的答案,而是评论。我会添加它作为评论,但我没有足够的“声誉点”。

对于它的价值,这是一个想要做这种事情的合理(我认为)用例。

我的应用程序使用 gtk,并旋转了很多线程。任何同时完成这两项操作的人都知道,您不能在主线程之外触摸 GUI。一个典型的解决方法是将接触 GUI 的可调用对象交给idle_add(),后者稍后会在安全的主线程中运行它。所以我有很多 次出现:

def threaded_gui_func(self, arg1, arg2):
    if threading.currentThread().name != 'MainThread':
        gobject.idle_add(self.threaded_gui_func, arg1, arg2)
        return
    # code that touches the GUI

如果我能做到的话,它会更短更容易(并且更有利于剪切和粘贴)

def thread_gui_func(self, arg1, arg2):
    if idleIfNotMain(): return
    # code that touches the GUI

如果我们在主线程中,idleIfNotMain() 只会返回 False,但如果不是,它会使用检查(或其他)找出可调用对象和 args 以移交给idle_add(),然后返回 True。得到我能弄清楚的参数。获取可调用对象似乎不太容易。 :-(

【讨论】:

  • 呃,没关系。 idleIfNotMain 正是装饰器的用途。
【解决方案2】:

为了支持所有情况,包括作为类的一部分或只是一个全局函数的函数,没有直接的方法可以做到这一点。您也许可以获取完整的调用堆栈并通过globals() 向下迭代,但这不会很好...

我能找到的最接近你的是:

import sys, types

def magic():
    # Get the frame before the current one (i.e. frame of caller)
    frame = sys._getframe(1)
    # Default values and closure is lost here (because they belong to the
    # function object.)
    return types.FunctionType(frame.f_code, frame.f_globals)

class MyClass(object):
    def foo(self, bar='Hello World!'):
        print bar
        return magic()

test = MyClass()

new_foo = test.foo()
new_foo(test, 'Good Bye World!')

您将执行完全相同的代码,但它将位于新的代码包装器中(例如,FunctionType。)

我怀疑您希望能够基于堆栈恢复应用程序的状态...这里的函数至少会与原始调用尽可能相似地调用函数(闭包仍然被忽略,因为如果您可以从框架中获取闭包,那么获取被调用的函数将非常容易):

import sys, types

class MyClass(object):
    def __init__(self, temp):
        self.temp = temp

    def foo(self, bar):
        print self.temp, bar
        return sys._getframe()

def test(hello):
    print hello, 'World!'
    return sys._getframe()

def recall(frame):
    code = frame.f_code
    fn = types.FunctionType(
        code, frame.f_globals, code.co_name,
        # This is one BIG assumption that arguments are always last.
        tuple(frame.f_locals.values()[-code.co_argcount:]))
    return fn()


test1 = MyClass('test1')
frame1 = test1.foo('Hello World!')

test2 = MyClass('test2')
frame2 = test2.foo('Good Bye World!')
frame3 = test2.foo('Sayonara!')

frame4 = test('HI')

print '-'

recall(frame4)
recall(frame3)
recall(frame2)
recall(frame1)

【讨论】:

  • 这个解决方案是基于这样一个假设,即底层的可调用对象是一个在全局范围内定义的函数——例如,对象方法没有被正确处理,因为 co_name 只返回这个方法的名称。跨度>
  • 我明白了。我不能说我建议你做你正在做的任何事情,但我现在用我能想到的最接近的解决方案更新了我的答案。
【解决方案3】:

有点难看,但在这里:

frame.f_globals[frame.f_code.co_name]

完整示例:

#!/usr/bin/env python

import sys

def foo():
  frame = sys._getframe()
  x = frame.f_globals[frame.f_code.co_name]

  print foo is x

foo()

打印“真”。

【讨论】:

  • +1:我相信frame.f_globals 比globals() 更正确。你的意思是print foo is x吗?
  • 从 Blixt 的回答中获得我的评论——如果 f_globals 中不存在可调用对象——比如对象方法,该怎么办?不过,感谢您的回答。
  • @Blixt 啊,是的,我的意思是'print foo is x'。在那里编辑。 @Mike 是的,如果它不是全局的,这将不起作用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-16
  • 1970-01-01
  • 1970-01-01
  • 2014-08-19
  • 2019-08-01
  • 2023-04-06
相关资源
最近更新 更多