在Python中,确定函数是否调用另一个函数

时间:2012-08-17 21:27:49

标签: python introspection

如果一个函数调用另一个函数,我如何以编程方式确定?我不能修改任何一个功能。

这就是我想要的(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的定义中。它可能会或可能不会实际调用它,具体取决于条件语句。

5 个答案:

答案 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))

问题在于,实际上无法判断某个函数是调用另一个函数还是只是加载对它的引用;如果另一个函数传递给mapreduce等,那么它将被调用但传递给它可能不是的另一个函数。实际上,明智的做法是假设如果函数在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将为您提供帮助