使用setattr装饰其他模块函数失效,如何修正代码?
问题原因分析
你遇到的核心问题是函数引用的拷贝与模块内函数的更新不同步:
- 当执行
from foo import bar时,主模块的bar变量是对foo模块中原始bar函数的引用拷贝。 mydecorator通过setattr修改了foo模块内的bar函数,但主模块的bar变量仍指向未装饰的原始函数。- 同时,
mydecorator内部循环始终检查传入的旧函数引用(主模块的bar),而非foo模块中已更新的装饰后函数,导致每次都判定为未缓存。
修正方案
方案一:让mydecorator每次读取模块内最新函数
修改deco.py,在循环中每次从原模块获取当前的函数实例,而非依赖传入的旧引用:
from functools import lru_cache import sys def mydecorator(somefunction): func_name = somefunction.__name__ func_module = sys.modules[somefunction.__module__] for _ in range(2): # 每次从模块中读取最新的函数实例 current_func = getattr(func_module, func_name) if hasattr(current_func, "cache_info"): print(f"{func_name} is already cached") else: print(f"caching {func_name}") decorated_func = lru_cache(current_func) setattr(func_module, func_name, decorated_func)
运行后输出将符合预期:
caching bar bar is already cached
注意:此方案仅修正了mydecorator的判断逻辑,主模块的bar变量仍指向原始函数。若后续要在主文件中调用缓存后的bar,需要手动更新主模块的引用。
方案二:同时更新主模块的函数引用
如果希望主模块的bar也直接指向缓存后的函数,可让mydecorator返回装饰后的函数,在主文件中重新赋值:
1. 修改deco.py
from functools import lru_cache import sys def mydecorator(somefunction): func_name = somefunction.__name__ func_module = sys.modules[somefunction.__module__] current_func = getattr(func_module, func_name) if hasattr(current_func, "cache_info"): print(f"{func_name} is already cached") return current_func else: print(f"caching {func_name}") decorated_func = lru_cache(current_func) setattr(func_module, func_name, decorated_func) return decorated_func
2. 修改主文件
from foo import bar from deco import mydecorator def main(): global bar # 重新赋值bar,指向缓存后的函数 bar = mydecorator(bar) # 验证两次状态 for _ in range(2): if hasattr(bar, "cache_info"): print(f"{bar.__name__} is already cached") else: bar = mydecorator(bar) if __name__ == "__main__": main()
此方案既修正了mydecorator的逻辑,也让主模块的bar直接使用缓存后的版本,完全符合预期行为。
内容的提问来源于stack exchange,提问作者Anthony Labarre
相关产品推荐
相关产品推荐

