2010-09-19 4 views
6

Я пишу код для определения имени, которому назначен объект. Это для общей работы по отладке и для дальнейшего ознакомления с внутренними компонентами python.Доступ к имени, которое присваивается создаваемому объекту,

У меня есть он структурированный как декоратор класса, так что все экземпляры этого класса будут иметь свои имена, записанные, если это возможно. Код довольно длинный, поэтому я не буду публиковать его, если не задано. Общая методика заключается в следующем, хотя

  1. украшают класс __init__ метод с кодом, чтобы делать то, что я хочу

  2. набор caller = inspect.currentframe().f_back и открытый inspect.getframeinfo(caller).filename и отправить его в ast.parse. Я не делаю никаких ошибок здесь, потому что (1) это просто для отладки/профилирования/взлома (2) этот точный процесс был «просто» завершен или код не будет работать. Есть ли проблема с этим?

  3. найти ast.Assignment экземпляр, который вызывает в настоящее время выполняет __init__ метод для запуска

  4. если len(assignment.targets) == 1 то есть только один пункт на левой стороне, и я могу получить имя из targets[0].id. В простой форме, например a = Foo(), тогда assignment.value является экземпляром ast.Call. если это буквальный список (например, список), то value будет тем списком и залогом, потому что объект, который мне интересен, не присваивается имени.

Что это лучший способ, чтобы подтвердить, что assignment.value.func фактически type(obj).__call__ объекта, который меня интересует. Я уверен, что я гарантировал, что это «где-то там» или код даже не будет работать. Мне просто нужно, чтобы он был на высшем уровне. Очевидное, что нужно сделать, это прогуляться и убедиться, что он не содержит внутренних вызовов. Тогда мне гарантировано, что у меня есть имя. (Мое рассуждение правильное, я не уверен, что его предположения). Это не идеально, потому что, если меня интересует Foo, это может привести меня отбросить a = Foo(Bar()), потому что я не знаю, если это a = Bar(Foo()).

Конечно, я могу просто проверить assignment.value.func.id, но потом кто-то мог бы сделать Foobar = Foo или что-то, так что я не хочу полагаться на это слишком сильно

Любая помощь будет принята с благодарностью. Как всегда, меня интересуют любые другие предложения или проблемы, которые я могу игнорировать.

Кроме того, я очень удивлен, что мне просто нужно было придумать тег 'python-internals'.

+0

+ много - Python классный! – katrielalex

ответ

2

АСТ не может дать вам этот ответ. Попробуйте использовать frame.f_lasti, а затем загляните в байт-код. Если следующая строка не является STORE_FAST, у вас есть внутренние звонки или что-то еще происходит иначе, чем простое задание, которое вы ищете.

def f(): 
    f = sys._getframe() 
    i = f.f_lasti + 3 # capture current point of execution, advance to expected store 
    print dis.disco(f.f_code, i) 
0

Я не знаю, как много помощи это, но вы считали, используя вызов locals()? Он возвращает dict, который содержит имя и значение всех локальных переменных.

Например:

s = '' 
locals() 
>>> {'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 's': '', '__name__': '__main__', '__doc__': None} 
t = s # I think this is what is of most importance to you 
locals() 
>>> {'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 's': '', 't': '', '__name__': '__main__', '__doc__': None} 

Таким образом, вы могли бы пройти этот словарь и проверить, какие переменные имеют (как их значения) объект типа, который вы ищете.

Как я уже сказал, я не знаю, насколько поможет этот ответ, но если вам нужно разъяснить что-либо, тогда оставляйте комментарий, и я постараюсь ответить как можно лучше.

+0

Это не работает, потому что 'locals()' всегда ссылается на фрейм, в который он вызван, я ищу один фрейм. Я могу получить это с помощью 'sys._getframe()' для 'inspect.currentfrmae'. Проблема в том, что 'foo = bar()' не создает запись в 'locals()' (ссылаясь на фрейм, в котором выполняется присваивание) до тех пор, пока _after_ 'bar .__ init __()' не вернется. Но это подходящее место для получения имени, потому что я могу просто сделать это с помощью декоратора на 'bar' вместо того, чтобы ставить код после _every_ назначения. – aaronasterling

+0

@AaronMcSmooth: Был бы вызов globals() вместо locals исправить эту проблему – inspectorG4dget

+1

@ InspectoG4det Нет по той же причине. 'globals' - это просто' frame.f_locals' для самого внешнего фрейма, и поэтому запись не является местом в 'frame.f_locals', пока внутренний фрейм (' bar .__ init __() 'в этом случае) не возвращается. – aaronasterling

0

Здесь я не проверяю ошибок, потому что (1) это просто для отладки/профилирования/взлома (2) этот точный процесс был «просто» завершен или код не будет запущен. Есть ли проблема с этим?

Да:

  1. запустить программу

  2. Wait блок импортирует определенный модуль foo.py

  3. Редактировать foo.py

сейчас код, загруженный в Python процесс не соответствует коду, найденному на диске.

Еще одна причина, по которой разбор байт-кода может быть лучшей техникой.

0

Вот как это делается. Многое благодаря анонимному подсказку. Большая удача в вашем стремлении собрать репутацию для вашей учетной записи alt.

import inspect 
import opcode 


def get_name(f): 
    """Gets the name that the return value of a function is 
    assigned to. 

    This could be modified for classes as well. This is a 
    basic version for illustration that only prints out 
    the assignment instead of trying to do anything with it. 
    A more flexible way would be to pass it a callback for when 
    it identified an assignment. 

    It does nothing for assignment to attributes. The solution 
    for that isn't much more complicated though. If the 
    instruction after the function call is a a `LOAD_GLOBAL`, 
    `LOAD_FAST` or `LOAD_DEREF`, then it should be followed by 
    a chain of `LOAD_ATTR`'s. The last one is the attribute 
    assigned to. 
    """ 

    def inner(*args, **kwargs): 
     name = None 

     frame = inspect.currentframe().f_back 
     i = frame.f_lasti + 3 

     # get the name if it exists 
     code = frame.f_code 
     instr = ord(code.co_code[i]) 
     arg = ord(code.co_code[i+1]) # no extended arg here. 
     if instr == opcode.opmap['STORE_FAST']: 
      name = code.co_varnames[arg] 
     elif instr in (opcode.opmap['STORE_GLOBAL'], 
         opcode.opmap['STORE_NAME']): 
      name = code.co_names[arg] 
     elif instr == opcode.opmap['STORE_DEREF']: 
      try: 
       name = code.co_cellvars[arg] 
      except IndexError: 
       name = code.co_freevars[arg - len(code.co_cellvars)] 
     ret = f(*args, **kwargs) 
     print opcode.opname[instr] 
     if name: 
      print "{0} = {1}".format(name, ret) 
     return ret 

    return inner 


@get_name 
def square(x): 
    return x**2 

def test_local(): 
    x = square(2) 

def test_deref(): 
    x = square(2) 
    def closure(): 
     y = x 
    return closure 

x = square(2) 
test_local() 
test_deref()() 

Оно не должно быть слишком трудно выяснить присвоения для list_[i] = foo() либо, в том числе стоимости i с использованием frame.f_locals. Трудные из них будут литералами, и когда они будут переданы в качестве аргумента. Оба этих случая должны быть довольно сложными.

 Смежные вопросы

  • Нет связанных вопросов^_^