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”标签。

4

4 回答 4

2

AST 不能给你那个答案。尝试使用 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)
于 2010-09-25T00:26:46.173 回答
0

我在这里不做任何错误检查,因为(1)这只是为了调试/分析/黑客(2)这个确切的过程“刚刚”完成,或者代码不会运行。这有问题吗?

是的:

  1. 启动程序

  2. 等待它导入特定模块 foo.py 的单元

  3. 编辑 foo.py

现在,在 Python 进程中加载​​的代码与磁盘上的代码不匹配。

反汇编字节码的另一个原因可能是一种更好的技术。

于 2010-09-28T02:35:26.023 回答
0

我不知道这有多大帮助,但是您是否考虑过调用 to locals()?它返回dict包含所有局部变量的名称和值的 a。

例如:

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}

因此,您可以遍历该字典并检查哪些变量(作为它们的值)具有您正在寻找的类型的对象。

就像我说的,我不知道这个答案有多大帮助,但是如果您需要澄清任何事情,请发表评论,我会尽我所能做出回应。

于 2010-09-26T13:13:17.633 回答
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()()

找出 for 的赋值应该不难list_[i] = foo(),包括iby using的值frame.f_locals。棘手的将是文字,当它作为参数传递时。这两种情况都应该是相当具有挑战性的。

于 2010-11-16T02:28:25.013 回答