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() を使用した簡単な例を次に示します。機能するには、ソース関数を呼び出す必要があります。また、まれに 2 つの異なる関数が同じコード オブジェクトを共有する場合があるため、動作が保証されるわけではありません。

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 に答える