访问正在创建的对象所赋予的名称
我正在写一些代码,用来确定一个对象被赋予的名称。这是为了进行一般的调试工作,同时也让我更熟悉Python的内部机制。
我把它做成了一个类装饰器,这样这个类的所有实例在可能的情况下都会记录它们的名称。代码比较长,所以我不会在这里贴出来,除非有人要求。大致的步骤如下:
用我想要的代码装饰类的
__init__
方法。设置
caller = inspect.currentframe().f_back
,然后打开inspect.getframeinfo(caller).filename
,把它传给ast.parse
。我在这里不做错误检查,因为(1)这只是为了调试/分析/黑客行为,(2)这个过程刚刚完成,否则代码根本不会运行。这样做有问题吗?找到导致当前执行的
__init__
方法运行的ast.Assignment
实例。如果
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 个回答
我这里没有做任何错误检查,因为(1)这只是为了调试/性能分析/黑客实验(2)这个过程刚刚完成,否则代码就不会运行。这样做有问题吗?
有:
启动一个程序
等到它导入一个特定的模块 foo.py
编辑 foo.py
现在,加载到 Python 进程中的代码和磁盘上的代码不一致了。
这也是为什么反汇编字节码可能是更好的一种方法。
我不知道这对你有多大帮助,但你有没有考虑过使用 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}
这样你就可以遍历这个字典,检查哪些变量的值是你想要的那种类型的对象。
就像我说的,我不知道这个回答能帮上你多少,但如果你对任何内容有疑问,欢迎留言,我会尽量回复你。
抽象语法树(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)