如何按调用顺序重执行类方法并保留传入参数?
方法调用追踪与重执行的优雅实现
需求背景
现有如下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
存在的问题:
- 自定义参数无法复现:如果调用时传入非默认参数(如
test.test1(arg1='foo', arg2='bar')),重执行时仍会使用默认参数 - 原方案不支持同一方法多次调用(虽然后续改用字典列表解决,但核心参数记录问题未解决)
优雅解决方案
核心思路是记录调用的方法名、位置参数和关键字参数,而非直接保存函数对象,重执行时通过方法名动态获取函数并传入保存的参数:
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
相关产品推荐
相关产品推荐

