2

如何以编程方式确定一个函数是否调用另一个函数?我无法修改任何一个功能。

这是我想要的(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. 根据条件语句,它可能会或可能不会真正调用它。

4

5 回答 5

5

如果您可以保证可以访问源代码,则可以使用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))

问题是实际上不可能判断一个函数是在调用另一个函数还是只是在加载对它的引用。如果另一个函数被传递给maporreduce等​​等,那么它将被调用,但它可能不会被传递给另一个函数。实际上,明智的做法是假设如果函数在,source.func_code.co_names那么它可能会被调用:

'target' in source.func_code.co_names
于 2012-08-17T22:23:38.183 回答
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)
于 2012-08-17T21:54:06.433 回答
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]
于 2012-08-17T21:50:36.147 回答
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
于 2012-08-17T22:15:41.197 回答
1

这篇文章http://stefaanlippens.net/python_inspect会帮助你

于 2012-08-17T22:26:34.593 回答