如何以编程方式确定一个函数是否调用另一个函数?我无法修改任何一个函数。
这是我想要的(source_calls_target):
>>> def check():
>>> pass
>>> def test_check():
>>> check()
>>> def source_calls_target(source, target):
>>> # if source() calls target() somewhere, return True
>>> ???
>>> source_calls_target(test_check, check)
True
>>> source_calls_target(check, test_check)
False
理想情况下,我不想实际调用 target()。
理想情况下,我想检查对 target() 的调用是否出现在 source 的定义中。它可能会或可能不会实际调用它,具体取决于条件语句。
请您参考如下方法:
如果你能保证可以访问源代码,你可以使用ast.parse:
import ast
call_names = [c.func.id for c in ast.walk(ast.parse(inspect.getsource(source)))
if isinstance(c, ast.Call)]
return 'target' in call_names
请注意,调用始终按名称进行,因此很难(而且可能无法)判断调用是针对特定函数还是同名函数。
在没有源代码的情况下,唯一的办法就是通过反汇编:
import dis
def ops(code):
i, n = 0, len(code)
while i < n:
op = ord(code[i])
i += 1
if op == dis.EXTENDED_ARG:
ext = ord(code[i]) + ord(code[i+1])*256
op = ord(code[i + 2])
i += 3
else:
ext = 0
if op >= dis.HAVE_ARGUMENT:
arg = ord(code[i]) + ord(code[i+1])*256 + ext*65536
i += 2
yield op, arg
else:
yield op, None
source_ops = list(ops(source.func_code.co_code))
问题是实际上不可能判断一个函数是调用另一个函数还是只是加载对它的引用;如果另一个函数传递给 map 或 reduce 等,那么它将被调用但传递给另一个函数可能不会。实际上,明智的做法是假设如果函数在 source.func_code.co_names 中,那么它可能被调用:
'target' in source.func_code.co_names
