从框架中获取可调用对象

发布于 2024-07-27 18:46:47 字数 993 浏览 3 评论 0原文

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

代码解释:

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

    # x is foo, now

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

希望这是可能的。

干杯,

MH

编辑:

我已经在某种程度上设法解决了这个问题。 它深受安德烈亚斯和亚历山大的回复的启发。 感谢大家投入的时间!

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() 中的所有内容,以搜索给定函数对象的可调用对象。

对我来说感觉有点不Python,但有效。

再次感谢!

MH

Given the frame object (as returned by sys._getframe, for instance), can I get the underlying callable object?

Code explanation:

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

    # x is foo, now

Note that my problem is getting the object out of a frame, not the currently called object.

Hope that's possible.

Cheers,

MH

EDIT:

I've somewhat managed to work around this problem. It was heavily inspired by Andreas' and Alexander's replies. Thanks guys for the time invested!

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

(works in 2.6.2, for py3k replace func_code with __code__ and im_func with __func__)

Then, I can aggressively traverse globals() or gc.get_objects() and dir() everything in search for the callable with the given function object.

Feels a bit unpythonic for me, but works.

Thanks, again!

MH

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

皓月长歌 2024-08-03 18:46:47

为了支持所有情况,包括函数作为类的一部分或只是全局函数,没有直接的方法来执行此操作。 您也许能够获得完整的调用堆栈并通过 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)

To support all cases, including the function being part of a class or just a global function, there is no straight-forward way of doing this. You might be able to get the complete call stack and iterate your way down through globals(), but it wouldn't be nice...

The closest I can get you is this:

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!')

You'll be executing the exact same code, but it'll be in a new code wrapper (e.g., FunctionType.)

I suspect you want to be able to restore the state of your application based on a stack... Here's something that will at least call the functions as similarly as possible to the original calls (the closure is still left out, because if you could get closures from the frames, getting the function that was called would be pretty easy):

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)
鱼忆七猫命九 2024-08-03 18:46:47

有点难看,但它是:

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()

打印“True”。

A little ugly but here it is:

frame.f_globals[frame.f_code.co_name]

Full example:

#!/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()

Prints 'True'.

半窗疏影 2024-08-03 18:46:47

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

就其价值而言,这是一个想要做此类事情的合理(我认为)用例。

我的应用程序使用 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,如果我们位于主线程中,但如果不在主线程中,它会使用检查(或其他方式)来找出可调用对象和参数以将其移交给 idle_add(),然后返回 True。 获取参数我可以弄清楚。 获得可调用似乎不太容易。 :-(

Not really an answer, but a comment. I'd add it as a comment, but I don't have enough "reputation points".

For what it's worth, here's a reasonable (I think) use case for wanting to do this sort of thing.

My app uses gtk, and spins a lot of threads. As anyone whose done both of these at once knows, you can't touch the GUI outside the main thread. A typical workaround is to hand the callable that will touch the GUI to idle_add(), which will run it later, in the main thread, where it is safe. So I have a lot of occurrences of:

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

It would be just a bit shorter and easier (and more conducive to cut-n-paste) if I could just do

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

where idleIfNotMain() just returns False if we are in the main thread, but if not, it uses inspect (or whatever) to figure out the callable and args to hand off to idle_add(), then returns True. Getting the args I can figure out. Getting the callable appears not to be too easy. :-(

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文