访问分配正在创建的对象的名称

时间:2010-09-19 06:49:32

标签: python python-internals

我正在编写一些代码来确定分配对象的名称。这是一般的调试工作,并进一步熟悉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.valueast.Call的实例。如果它是文字(例如列表),则value将是该列表并保释,因为我感兴趣的对象未被分配给名称。

  5. 确认assignment.value.func实际上是我感兴趣的对象的type(obj).__call__的最佳方法是什么。我很确定我保证它“在某处” “或者代码甚至不会运行。我只需要它在顶级。显而易见的事情是走它并确保它不包含任何内部调用。然后我保证我有这个名字。 (我的推理是正确的,我不确定它的假设是否正确)。这并不理想,因为如果我对Foo感兴趣,这可能会让我抛弃a = Foo(Bar())因为我不知道它是a = Bar(Foo())

    当然我可以查看assignment.value.func.id但是有人可以做Foobar = Foo或其他什么,所以我不想太依赖它

    非常感谢任何帮助。一如既往,我对我可能忽略的任何其他建议或问题感兴趣。

    另外,我真的很惊讶我不得不发明'python-internals'标签。

4 个答案:

答案 0 :(得分: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)

答案 1 :(得分: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}

因此,您可以遍历此字典并检查哪些变量(作为其值)是您要查找的类型的对象。

就像我说的那样,我不知道这个答案有多大帮助,但如果你需要澄清任何事情,那么请留下评论,我会尽力回应。

答案 2 :(得分:0)

  

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

是:

  1. 启动程序

  2. 等待单位导入特定模块foo.py

  3. 编辑foo.py

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

    分解字节码的另一个原因可能是更好的技术。

答案 3 :(得分: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的值。棘手的是文字,当它作为参数传递时。这两种情况都应该是非常具有挑战性的。