如何从经组合装饰器装饰的函数中获取参数?
现有场景需要通过组合后的remotable装饰器(由remotable_timer和remotable_cache组合而成)修饰test_function,但当前组合写法导致remotable_timer无法获取test_function的实际调用参数(如context、1、2、3),仅能拿到被包装的remotable_cache函数。要求保留组合装饰器的写法,解决参数获取问题。
原代码及输出如下:
import functools def remotable_timer(fn): @functools.wraps(fn) def wrapper(*args, **kwargs): print('remotable_timer', fn, args, **kwargs) result = fn(*args, **kwargs) return result wrapper.original_fn = fn return wrapper def remotable_cache(fn): @functools.wraps(fn) def wrapper(*args, **kwargs): print('remotable_cache', fn, args) result = fn(*args, **kwargs) return result wrapper.original_fn = fn return wrapper # 原组合写法问题:remotable_timer直接包装remotable_cache函数,而非形成装饰器链 remotable = remotable_timer(remotable_cache) class TestClass(object): @remotable def test_function(self, context, *args): print('test_function', context, *args) if __name__ == '__main__': test = TestClass() test.test_function('context', 1, 2, 3)
原输出:
remotable_timer <function remotable_cache at 0x000002780F7336A8> (<function TestClass.test_function at 0x000002780F733730>,) remotable_cache <function TestClass.test_function at 0x000002780F733730> (<__main__.TestClass object at 0x000002780F72D898>, 'context', 1, 2, 3) test_function context 1 2 3
修改组合装饰器的实现方式,让remotable成为一个接收目标函数的装饰器,内部先应用remotable_cache再应用remotable_timer,形成完整的装饰器调用链:
import functools def remotable_timer(fn): @functools.wraps(fn) def wrapper(*args, **kwargs): print('remotable_timer', fn.__name__, args, **kwargs) result = fn(*args, **kwargs) return result wrapper.original_fn = fn return wrapper def remotable_cache(fn): @functools.wraps(fn) def wrapper(*args, **kwargs): print('remotable_cache', fn.__name__, args) result = fn(*args, **kwargs) return result wrapper.original_fn = fn return wrapper # 修改组合逻辑:让remotable作为装饰器,先将目标函数传给remotable_cache,再传给remotable_timer def remotable(fn): return remotable_timer(remotable_cache(fn)) # 或用lambda简化写法:remotable = lambda fn: remotable_timer(remotable_cache(fn)) class TestClass(object): @remotable def test_function(self, context, *args): print('test_function', context, *args) if __name__ == '__main__': test = TestClass() test.test_function('context', 1, 2, 3)
修改后的输出:
remotable_timer wrapper (<__main__.TestClass object at 0x0000021A0D6AD898>, 'context', 1, 2, 3) {} remotable_cache test_function (<__main__.TestClass object at 0x0000021A0D6AD898>, 'context', 1, 2, 3) test_function context 1 2 3
原组合写法remotable = remotable_timer(remotable_cache)是将remotable_cache函数直接作为参数传给remotable_timer,此时remotable_timer的wrapper接收的是目标函数test_function(而非调用参数),导致无法获取实际调用时的参数。
修改后的组合逻辑,让remotable作为一个装饰器函数,先将目标函数test_function传入remotable_cache得到包装后的函数,再将这个包装函数传入remotable_timer,形成remotable_timer→remotable_cache→test_function的调用链。当test_function被调用时,参数会先进入remotable_timer的wrapper,从而实现参数的获取。
内容的提问来源于stack exchange,提问作者Yanchi De Zhang

