我正在编写一些代码来确定分配对象的名称。这是一般的调试工作,并进一步熟悉python内部。
我将它结构化为类装饰器,以便该类的所有实例都可以记录它们的名称。代码相当长,所以除非被问到,否则我不会发布。一般技术如下
使用代码装饰类{__init__
方法以执行我想要的操作
设置caller = inspect.currentframe().f_back
并打开inspect.getframeinfo(caller).filename
并将其发送至ast.parse
。我没有在这里做任何错误检查,因为(1)这只是用于调试/分析/黑客攻击(2)这个确切的过程“刚刚”完成或代码不会运行。这有问题吗?
找到导致当前正在执行ast.Assignment
方法的__init__
实例
如果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 :(得分: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)这个确切的过程“刚刚”完成或者代码不会运行。这有问题吗?
是:
启动程序
等待单位导入特定模块foo.py
编辑foo.py
现在,在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
的值。棘手的是文字,当它作为参数传递时。这两种情况都应该是非常具有挑战性的。