Доступ к имени, которому назначен создаваемый объект - PullRequest
6 голосов
/ 19 сентября 2010

Я пишу некоторый код, чтобы определить имя, которому присвоен объект.Это для общей работы по отладке и для дальнейшего знакомства с внутренними компонентами 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'.

Ответы [ 4 ]

2 голосов
/ 25 сентября 2010

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

0 голосов
/ 16 ноября 2010

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

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.Хитрые будут буквальными и когда это передается в качестве аргумента.Оба эти случая должны быть довольно сложными.

0 голосов
/ 28 сентября 2010

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

Да:

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

  2. Подождите, пока она импортируетконкретный модуль foo.py

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

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

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

0 голосов
/ 26 сентября 2010

Я не знаю, сколько это поможет, но вы рассматривали возможность использования звонка на 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}

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

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

...