Получение вызываемого объекта из кадра - PullRequest
3 голосов
/ 15 июля 2009

Учитывая объект фрейма (как, например, возвращается sys._getframe ), могу ли я получить базовый вызываемый объект?

Объяснение кода:

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

    # x is foo, now

Обратите внимание, что моя проблема - получить объект из фрейма, а не вызываемый в данный момент объект.

Надеюсь, что это возможно.

Приветствия

MH

EDIT:

Мне несколько удалось обойти эту проблему. Он был сильно вдохновлен ответами Андреаса и Александра. Спасибо, ребята, за потраченное время!

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

Ответы [ 3 ]

1 голос
/ 15 июля 2009

Немного некрасиво, но вот оно:

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'.

1 голос
/ 15 июля 2009

Для поддержки всех случаев, включая функцию, являющуюся частью класса или просто глобальной функцией, не существует прямого способа сделать это. Возможно, вы сможете получить полный стек вызовов и пройти через 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)
0 голосов
/ 27 июня 2015

Не совсем ответ, но комментарий. Я бы добавил это как комментарий, но у меня не хватает «очков репутации».

Для того, чтобы это стоило, вот разумный (я думаю) вариант использования для того, чтобы делать подобные вещи.

Мое приложение использует 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

Было бы немного короче и проще (и более подходящим для cut-n-paste), если бы я мог просто сделать

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

, где idleIfNotMain () просто возвращает False, если мы находимся в главном потоке, но если нет, он использует inspect (или любой другой) для определения вызываемого объекта и аргументы для передачи idle_add(), затем возвращает True Получить аргументы я могу понять. Получение вызова, кажется, не слишком легко. : - (

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...