You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python执行过程中获取某一函数调用的所有函数列表?

解决方案:追踪函数调用同时保留签名完整性

针对你需要追踪ConversionFunctions库中所有被调用函数(包括嵌套调用),同时还要保留原函数签名用于参数校验的需求,这里有几个可行的方案:


方案一:改进装饰器,保留原函数签名

装饰器导致签名变化的问题其实很容易解决,Python标准库的functools.wraps可以帮我们完整保留原函数的元数据(包括签名、函数名、文档字符串等)。这个方案逻辑直观,性能也不错,适合能修改ConversionFunctions模块的场景。

实现步骤:

  1. 编写一个追踪装饰器,用functools.wraps包装被装饰函数,确保签名不变
  2. 用线程局部存储维护调用记录,避免多线程环境下的冲突
  3. 自动给ConversionFunctions中的所有函数应用装饰器
  4. 在转换节点时重置记录、执行函数、提取调用列表

示例代码:

import functools
import inspect
import threading
from typing import Set

# 线程局部存储,确保多线程下追踪记录互不干扰
call_tracker = threading.local()
call_tracker.called_functions = set()

def track_conversion_function(func):
    @functools.wraps(func)  # 关键:完全保留原函数的签名和元数据
    def wrapper(*args, **kwargs):
        # 仅追踪ConversionFunctions模块下的函数
        if inspect.getmodule(func).__name__ == 'ConversionFunctions':
            call_tracker.called_functions.add(func.__name__)
        return func(*args, **kwargs)
    return wrapper

# 自动给ConversionFunctions中的所有函数添加追踪装饰器
import ConversionFunctions
for name, obj in inspect.getmembers(ConversionFunctions, inspect.isfunction):
    setattr(ConversionFunctions, name, track_conversion_function(obj))

def convert_node(node_info):
    # 重置当前节点的追踪记录
    call_tracker.called_functions.clear()
    
    node_conversion_fxn = getattr(ConversionFunctions, node_info['fxn_name'])
    
    # 正常做参数签名校验,完全不受装饰器影响
    sig = inspect.signature(node_conversion_fxn)
    try:
        sig.bind(**node_info['arguments'])
    except TypeError as e:
        print(f"参数不匹配:{e}")
        return
    
    node_conversion_fxn(**node_info['arguments'])
    
    # 获取当前节点转换过程中调用的所有函数
    called_funcs = call_tracker.called_functions.copy()
    print(f"当前节点调用的函数:{called_funcs}")
    return called_funcs

方案二:使用sys.settrace无侵入式追踪

如果你无法修改ConversionFunctions模块的代码,可以用Python内置的sys.settrace钩子实现全局追踪。这个方案完全不需要修改原函数,不会影响任何签名,但需要注意在使用后恢复原追踪逻辑,避免干扰其他代码。

实现步骤:

  1. 定义追踪函数,仅记录ConversionFunctions模块下的函数调用
  2. 在转换节点前后开启/关闭追踪,确保只追踪当前节点的转换过程
  3. 用上下文管理器封装追踪逻辑,让代码更优雅

示例代码:

import sys
import inspect
from contextlib import contextmanager
from typing import Set

class ConversionCallTracker:
    def __init__(self):
        self.called_functions: Set[str] = set()
    
    def _trace_handler(self, frame, event, arg):
        if event != 'call':
            return self._trace_handler
        
        # 仅追踪ConversionFunctions模块的函数
        module_name = frame.f_globals.get('__name__', '')
        if module_name == 'ConversionFunctions':
            self.called_functions.add(frame.f_code.co_name)
        
        return self._trace_handler
    
    @contextmanager
    def track(self):
        # 保存原追踪逻辑,避免干扰其他代码
        original_trace = sys.gettrace()
        sys.settrace(self._trace_handler)
        try:
            self.called_functions.clear()
            yield self
        finally:
            # 恢复原追踪逻辑
            sys.settrace(original_trace)

# 初始化追踪器
tracker = ConversionCallTracker()

def convert_node(node_info):
    node_conversion_fxn = getattr(ConversionFunctions, node_info['fxn_name'])
    
    # 参数签名校验不受任何影响
    sig = inspect.signature(node_conversion_fxn)
    try:
        sig.bind(**node_info['arguments'])
    except TypeError as e:
        print(f"参数不匹配:{e}")
        return
    
    # 仅在当前节点转换过程中开启追踪
    with tracker.track() as t:
        node_conversion_fxn(**node_info['arguments'])
        called_funcs = t.called_functions.copy()
    
    print(f"当前节点调用的函数:{called_funcs}")
    return called_funcs

总结建议

  • 如果可以修改ConversionFunctions模块,优先选方案一:装饰器性能更好,逻辑更直观,适合长期做函数使用情况的统计分析。
  • 如果不能修改原模块,选方案二:完全无侵入,不会对原代码造成任何改动,上下文管理器的封装也让代码更整洁。
  • 两个方案都完整保留了原函数的签名,完全满足你做参数校验的需求,也能准确记录所有嵌套调用的函数,完美匹配你评估函数使用情况的目标。

内容的提问来源于stack exchange,提问作者Liz C.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 09:54:06