如何实现仅在DEBUG为False时生效的Python缓存装饰器?
解决方案:通用的开关式装饰器工厂
要实现一个能根据DEBUG状态启用/禁用原装饰器的通用工具,核心是要同时兼容装饰器带参数和不带参数的两种调用方式,并且不依赖参数是否可调用的判断逻辑。
问题根源
你原来的代码存在两个关键问题:
DEBUG=False时递归调用了prod_lru_cache而非实际的lru_cache,会直接导致栈溢出DEBUG=True时无法区分装饰器的两种调用场景——直接传入被装饰函数(无参数用法)还是传入配置参数(带参数用法),尤其是当参数本身是可调用对象时,靠callable判断会彻底失效
通用实现方案
我们可以编写一个通用的装饰器工厂,通过检查调用时的参数特征来适配两种使用场景,完全不依赖参数类型判断:
from functools import lru_cache DEBUG = True def conditional_decorator(debug_flag, real_decorator): def decorator(*args, **kwargs): # 场景1:装饰器不带参数,直接传入被装饰函数 if len(args) == 1 and len(kwargs) == 0 and callable(args[0]): func = args[0] return func if debug_flag else real_decorator(func) # 场景2:装饰器带参数,返回一个包装后的装饰器函数 else: def wrapper(func): return func if debug_flag else real_decorator(*args, **kwargs)(func) return wrapper return decorator # 基于lru_cache生成我们的开关式装饰器 prod_lru_cache = conditional_decorator(DEBUG, lru_cache)
验证两种使用场景
场景1:装饰器不带参数
@prod_lru_cache def foo(): print('foo') return 1 foo() # 打印'foo',DEBUG=True时无缓存,每次调用都执行 foo() # 再次打印'foo'
场景2:装饰器带参数
@prod_lru_cache(maxsize=7) def bar(): print('bar') return 2 bar() # 打印'bar' bar() # DEBUG=True时再次打印'bar',无缓存
当把DEBUG设为False时,上述两个函数都会正常启用lru_cache的缓存功能,第二次调用不会打印函数内的内容。
方案通用性说明
- 不依赖参数类型判断,通过调用时的参数数量和是否仅传入一个可调用对象区分场景,避免了参数为可调用对象时的误判
- 可适配任何装饰器(如
cache、staticmethod等),只需通过conditional_decorator包装原装饰器即可 - 逻辑清晰,完全覆盖装饰器的两种调用模式
简化版(仅针对lru_cache)
如果只需要为lru_cache实现该功能,可以直接把逻辑写入目标装饰器,无需通用工厂:
from functools import lru_cache DEBUG = True def prod_lru_cache(*args, **kwargs): if len(args) == 1 and not kwargs and callable(args[0]): # 无参数装饰器用法 func = args[0] return func if DEBUG else lru_cache(func) else: # 带参数装饰器用法 def decorator(func): return func if DEBUG else lru_cache(*args, **kwargs)(func) return decorator
内容的提问来源于stack exchange,提问作者run_the_race
相关产品推荐
相关产品推荐

