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

使用Python上下文管理器获取上下文内的函数调用顺序

这个需求完全可以实现。
你之前使用inspect.stack失败的核心原因是:inspect.stack只能获取调用时刻的当前调用栈信息,如果你是在上下文管理器的__exit__阶段才去查栈,此时上下文中前序执行的add、divide函数已经执行完成并退出调用栈,自然只能拿到最后执行的multiply的相关记录。

实现方案

方案1:轻量装饰器方案(无性能损耗,推荐)

如果允许对需要追踪的函数添加装饰器标记,这是最稳定易用的实现方式:

import functools

# 用于标记需要追踪的函数的装饰器
def track_call(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        # 当存在活跃的追踪上下文时记录调用
        if getattr(CallTracker, "active_context", None) is not None:
            CallTracker.active_context.append((func, args))
        return func(*args, **kwargs)
    return wrapper

# 上下文管理器实现
class CallTracker:
    active_context = None
    def __enter__(self):
        self.record = []
        CallTracker.active_context = self.record
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        CallTracker.active_context = None

使用方式如下:

# 给需要追踪的函数加上装饰器
@track_call
def add(a, b):
    return a + b

@track_call
def divide(a, b):
    return a / b

@track_call
def multiply(a, b):
    return a * b

# 调用示例
with CallTracker() as tracker:
    add(1, 2)
    divide(3, 4)
    multiply(5, 6)

print(tracker.record)
# 输出格式与需求一致:[(<function add at 0x...>, (1, 2)), (<function divide at 0x...>, (3, 4)), (<function multiply at 0x...>, (5, 6))]

方案2:无侵入追踪方案(无需修改原有函数)

如果不想对原有函数做任何修改,可以临时启用Python的系统追踪接口sys.settrace实现调用记录,注意该方案会产生微小的性能损耗:

import sys
import inspect

class NoIntrudeCallTracker:
    def __enter__(self):
        self.record = []
        self.context_frame = inspect.currentframe().f_back
        # 注册追踪函数
        sys.settrace(self.trace_func)
        return self
    def trace_func(self, frame, event, arg):
        # 只捕获上下文范围内的顶层函数调用
        if event == "call" and frame.f_back == self.context_frame:
            func_name = frame.f_code.co_name
            func = frame.f_globals.get(func_name, None)
            if func:
                args = tuple(frame.f_locals[name] for name in frame.f_code.co_varnames[:frame.f_code.co_argcount])
                self.record.append((func, args))
        return self.trace_func
    def __exit__(self, exc_type, exc_val, exc_tb):
        sys.settrace(None)

使用时直接包裹代码即可,无需修改原有函数定义:

with NoIntrudeCallTracker() as tracker:
    add(1, 2)
    divide(3, 4)
    multiply(5, 6)

print(tracker.record)

内容的提问来源于stack exchange,提问作者marwan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 23:15:04