如何在Python中获取函数调用名称与参数以分析依赖避免递归错误
函数依赖分析与递归问题解决方案
问题描述
我有一批仅接受单个参数x的函数,当函数数量较多时,计算过程中会出现RecursionError这类错误。我想提前理清函数之间的依赖关系,优先计算无依赖的函数并缓存结果,以此完成所有函数的计算。
示例代码:
def func_a(x): if x == 1: return func_b(x) return func_a(x-1) + func_c(x+1) def func_b(x): return x def func_c(x): return func_b(x)
需求目标
我希望传入类似(func_a, 5)的函数与参数元组,得到该函数会调用的所有函数及其对应参数,比如:
(func_a, 5) --> [(func_b, 4), (func_c, 6)]
其他示例:
(func_a, 1) --> [(func_b, 1)] (func_c, 3) --> [(func_b, 3)] (func_b, 1) --> []
已尝试方案
我用过@lru_cache装饰器,但解决不了RecursionError问题,执行print(func_a(500))会抛出:
RecursionError: maximum recursion depth exceeded
我还查过inspect模块,但没找到可行的解决办法。
解决方案
1. 静态分析函数依赖(基于AST)
通过Python的ast模块解析函数源代码,提取调用的其他函数及对应的参数值,无需执行函数即可分析依赖,从根源避免递归错误。
import ast import inspect def get_function_dependencies(func, x): # 获取函数源代码并解析成AST树 source = inspect.getsource(func) tree = ast.parse(source) dependencies = [] # 遍历AST节点寻找函数调用 for node in ast.walk(tree): if isinstance(node, ast.Call) and isinstance(node.func, ast.Name): called_func_name = node.func.id # 确认被调用函数在当前全局作用域中 if called_func_name in globals(): called_func = globals()[called_func_name] # 计算传入的参数值 try: local_vars = {'x': x} arg_value = eval(compile(ast.Expression(node.args[0]), '<ast>', 'eval'), globals(), local_vars) dependencies.append((called_func, arg_value)) except Exception: # 复杂参数逻辑可根据实际情况扩展处理 pass # 去重后返回 return list(set(dependencies))
测试效果:
print(get_function_dependencies(func_a, 5)) # 输出类似 [(<function func_c at 0x...>, 6), (<function func_b at 0x...>, 4)] print(get_function_dependencies(func_a, 1)) # 输出 [(<function func_b at 0x...>, 1)] print(get_function_dependencies(func_c, 3)) # 输出 [(<function func_b at 0x...>, 3)] print(get_function_dependencies(func_b, 1)) # 输出 []
2. 动态追踪函数调用(拦截递归执行)
如果静态分析无法覆盖复杂的分支逻辑,可使用sys.settrace追踪函数调用,拦截递归执行过程,只记录依赖关系不实际完成递归。
import sys def track_dependencies(target_func, x): dependencies = [] processed = set() def trace_handler(frame, event, arg): if event == 'call': func_name = frame.f_code.co_name current_x = frame.f_locals.get('x') # 只处理目标相关的自定义函数 if func_name in globals() and current_x is not None: current_func = globals()[func_name] key = (current_func, current_x) if key not in processed and key != (target_func, x): dependencies.append(key) processed.add(key) # 拦截当前函数执行,避免递归深入 return lambda *args, **kwargs: None return trace_handler # 设置追踪器并尝试调用目标函数 sys.settrace(trace_handler) try: target_func(x) except Exception: # 拦截后的返回会触发错误,直接忽略 pass finally: sys.settrace(None) return list(set(dependencies))
3. 基于依赖的迭代计算与缓存
拿到依赖关系后,用拓扑排序的思路迭代计算:优先处理无依赖的函数,缓存结果,确保计算过程不会触发递归。
from collections import deque def compute_with_dependencies(target_func, x): cache = {} task_queue = deque([(target_func, x)]) while task_queue: func, arg = task_queue.popleft() if (func, arg) in cache: continue deps = get_function_dependencies(func, arg) # 检查所有依赖是否已缓存 all_deps_ready = all(dep in cache for dep in deps) if all_deps_ready: # 安全计算并缓存结果 cache[(func, arg)] = func(arg) else: # 依赖未完成,重新加入队列,先处理依赖 task_queue.append((func, arg)) # 添加未处理的依赖到队列头部,优先计算 for dep in deps: if dep not in task_queue and dep not in cache: task_queue.appendleft(dep) return cache[(target_func, x)]
测试func_a(500):
print(compute_with_dependencies(func_a, 500)) # 不会触发RecursionError,顺利得到结果
内容的提问来源于stack exchange,提问作者zchmielewska
相关产品推荐
相关产品推荐

