如果一个函数调用另一个函数,我如何以编程方式确定?我不能修改任何一个功能。
这就是我想要的(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
的定义中。它可能会或可能不会实际调用它,具体取决于条件语句。
答案 0 :(得分:4)
如果您可以保证可以访问源代码,则可以使用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
答案 1 :(得分:3)
这是一个使用sys.settrace()的简单示例。它确实需要调用源函数才能工作。它也不能保证工作,因为在极少数情况下,两个不同的函数可能共享相同的代码对象。
import sys
def check():
pass
def test_check():
check()
def source_calls_target(source, target):
orig_trace = sys.gettrace()
try:
tracer = Tracer(target)
sys.settrace(tracer.trace)
source()
return tracer.called
finally:
sys.settrace(orig_trace)
class Tracer:
def __init__(self, target):
self.target = target
self.called = False
def trace(self, frame, event, arg):
if frame.f_code == self.target.func_code:
self.called = True
print source_calls_target(test_check, check)
print source_calls_target(check, test_check)
答案 2 :(得分:1)
可能非常丑陋,但有效:
import profile
def source_calls_target(source, target):
pro = profile.Profile()
pro.run(source.__name__ + '()')
pro.create_stats()
return target.__name__ in [ele[2] for ele in pro.stats]
答案 3 :(得分:1)
我一直在想出一些与@ Luke的好答案非常相似的东西。我只是一个简单的函数,但它检查目标函数的直接调用者是否是源函数:
import sys
from functools import partial
def trace_calls(call_list, frame, event, arg):
if event != 'call':
return
call_list.append(frame.f_code)
def test_call(caller, called):
traces = []
old = sys.gettrace()
sys.settrace(partial(trace_calls, traces))
try:
caller()
finally:
sys.settrace(old)
try:
idx = traces.index(called.func_code)
except ValueError:
return False
if idx and traces[idx-1] == caller.func_code:
return True
return False
测试......
def a():
b()
def b():
pass
def c():
a()
test_call(c,a) #True
test_call(a,b) #True
test_call(c,b) #False
答案 4 :(得分:1)
这篇文章http://stefaanlippens.net/python_inspect将为您提供帮助