更新:添加了Python2.7
测试并确认使用Python2.7、Python3.5和{}
dis输出)是我自己的:
设置:import dis
import sys
from contextlib import contextmanager
# setup test environment
def a(_,__):
pass
def b(_,__,___):
pass
def c(_):
pass
def g():
pass
d = 4
def test(flag):
e = c
if flag:
a(a(b,c), [l for l in g(1, x=2)])
else:
b(a, int(flag), c(e))
d = d + 1
def calculate(a, b, operator):
if operator == Operator.add:
add(a, b)
elif operator == Operator.subtract:
subtract(a, b)
class Operator(object):
add = "add"
subtract = "subtract"
Python 2/3兼容性:
^{pr2}$
代码:def get_function_calls(func, built_ins=False):
# the used instructions
ins = list(dis.get_instructions(func))[::-1]
# dict for function names (so they are unique)
names = {}
# go through call stack
for i, inst in list(enumerate(ins))[::-1]:
# find last CALL_FUNCTION
if inst.opname[:13] == "CALL_FUNCTION":
# function takes ins[i].arg number of arguments
ep = i + inst.arg + (2 if inst.opname[13:16] == "_KW" else 1)
# parse argument list (Python2)
if inst.arg == 257:
k = i+1
while k < len(ins) and ins[k].opname != "BUILD_LIST":
k += 1
ep = k-1
# LOAD that loaded this function
entry = ins[ep]
# ignore list comprehensions / ...
name = str(entry.argval)
if "." not in name and entry.opname == "LOAD_GLOBAL" and (built_ins or not hasattr(builtin, name)):
# save name of this function
names[name] = True
# reduce this CALL_FUNCTION and all its paramters to one entry
ins = ins[:i] + [entry] + ins[ep + 1:]
return sorted(list(names.keys()))
输出:> print(get_function_calls(test))
> ['a', 'b', 'c', 'g']
> print(get_function_calls(test, built_ins=True))
> ['a', 'b', 'c', 'g', 'int']
> print(get_function_calls(calculate))
> ['add', 'subtract']
由于Patrick Haugh关于^{}的评论已经超过2小时了,我认为这篇文章是免费的。。。