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

如何按调用顺序重执行类方法并保留传入参数?

方法调用追踪与重执行的优雅实现

需求背景

现有如下Test类,包含任意数量实例方法:

class Test:
    def __init__(self, foo):
        self.foo = foo

    def test1(self, arg1=1, arg2=2):
        # do something
        print(f"test1, arg1 = {arg1}, arg2 = {arg2}")

    def test2(self, arg3=3, arg4=4):
        # do something else
        print(f"test2, arg3 = {arg3}, arg4 = {arg4}")

需要实现两个核心功能:

  • 追踪该类实例的所有方法调用记录,包括调用顺序和传入的具体参数
  • 提供一个方法,按原调用顺序和传入参数重执行所有记录的方法

原有实现的问题

最初采用装饰器记录调用,但存在明显缺陷:

def set_stuff(func):
    def inner(self, *args, **kwargs):
        func_name = func.__name__
        self.called.append({f"{func_name}": func})
        func(self, *args, **kwargs)
    return inner


class Test:
    def __init__(self, foo):
        self.foo = foo
        self.called = list()

    @set_stuff
    def test1(self, arg1=1, arg2=2):
        print(f"test1, arg1 = {arg1}, arg2 = {arg2}")

    @set_stuff
    def test2(self, arg3=3, arg4=4):
        print(f"test2, arg3 = {arg3}, arg4 = {arg4}")

    def call_in_order(self):
        for func_order in self.called:
            func = list(func_order.values())[0]
            func(self)

执行示例:

test = Test('yo')

test.test2()
# 输出: test2, arg3 = 3, arg4 = 4

test.test1()
# 输出: test1, arg1 = 1, arg2 = 2

test.call_in_order()
# 输出: test2, arg3 = 3, arg4 = 4
# 输出: test1, arg1 = 1, arg2 = 2

存在的问题:

  1. 自定义参数无法复现:如果调用时传入非默认参数(如test.test1(arg1='foo', arg2='bar')),重执行时仍会使用默认参数
  2. 原方案不支持同一方法多次调用(虽然后续改用字典列表解决,但核心参数记录问题未解决)

优雅解决方案

核心思路是记录调用的方法名、位置参数和关键字参数,而非直接保存函数对象,重执行时通过方法名动态获取函数并传入保存的参数:

def track_calls(func):
    def wrapper(self, *args, **kwargs):
        # 记录调用的关键信息:方法名、位置参数、关键字参数
        self.call_history.append({
            'method_name': func.__name__,
            'args': args,
            'kwargs': kwargs
        })
        # 执行原方法
        return func(self, *args, **kwargs)
    return wrapper


class Test:
    def __init__(self, foo):
        self.foo = foo
        # 初始化调用历史列表
        self.call_history = []

    @track_calls
    def test1(self, arg1=1, arg2=2):
        print(f"test1, arg1 = {arg1}, arg2 = {arg2}")

    @track_calls
    def test2(self, arg3=3, arg4=4):
        print(f"test2, arg3 = {arg3}, arg4 = {arg4}")

    def replay_calls(self):
        """按原顺序重执行所有记录的方法调用"""
        for call_info in self.call_history:
            method = getattr(self, call_info['method_name'])
            method(*call_info['args'], **call_info['kwargs'])

测试验证

test = Test('hello')

# 模拟多次带自定义参数的调用
test.test1(arg1='foo', arg2='bar')
# 输出: test1, arg1 = foo, arg2 = bar

test.test2(arg4=100)
# 输出: test2, arg3 = 3, arg4 = 100

test.test1(arg1=5)
# 输出: test1, arg1 = 5, arg2 = 2

print("--- 开始重执行 ---")
test.replay_calls()
# 输出: test1, arg1 = foo, arg2 = bar
# 输出: test2, arg3 = 3, arg4 = 100
# 输出: test1, arg1 = 5, arg2 = 2

方案优势

  • 参数完整复现:无论调用时使用默认参数、位置参数还是关键字参数,都能精准记录并重执行
  • 天然支持多次调用:同一方法的每次调用都会生成独立的记录,重执行时按顺序复现
  • 逻辑解耦:装饰器仅负责记录调用信息,重执行逻辑独立,代码更清晰可维护
  • 低侵入性:只需给需要追踪的方法添加装饰器,不修改原方法的核心逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 09:05:23