如何修改包变量以改变Python装饰器的行为?
解决动态修改装饰器最小样本量不生效的问题
这个问题的核心在于装饰器是在模块加载时就完成绑定的:当你导入pymetrics模块时,@minSample(MIN_SIZE)这行代码会立即执行,此时MIN_SIZE的初始值10被直接“固化”到了装饰器内部的闭包中。之后你修改mtr.MIN_SIZE=0,只是改变了模块级别的变量,但已经装饰好的inventory函数内部,判断条件用的还是最初绑定的10,所以才会依然返回None。
下面给你两种可行的解决方案,你可以根据需求选择:
方案一:用可变对象包装样本量,实现全局统一修改
因为整数是不可变类型,装饰器捕获的是它的值而非引用,我们可以用一个可变容器(比如字典)来存储样本量,这样装饰器每次都会读取容器里的最新值:
步骤1:修改pymetrics.py
把MIN_SIZE改成字典形式,让它成为可变对象:
from .utils import minSample # 用字典包装样本量,方便后续动态修改 MIN_SIZE = {'value': 10} @minSample(MIN_SIZE) def inventory(df): '''for quantisation''' return len(df) # 其他使用@minSample的函数也统一改成@minSample(MIN_SIZE)
步骤2:修改utils.py的装饰器
让装饰器读取容器里的value字段,而不是直接使用传入的数值:
from functools import wraps def minSample(sample_container): def decorator(method): @wraps(method) def f(*args, **kwargs): # 每次调用都读取容器里的最新值 if len(args[0]) < sample_container['value']: return None return method(*args, **kwargs) return f return decorator
生产代码中修改样本量
现在你只需要修改字典里的value,所有被装饰的函数都会自动使用新的样本量:
import pymetrics as mtr # 修改全局最小样本量为0 mtr.MIN_SIZE['value'] = 0 # 此时调用inventory会按新的样本量判断 result = mtr.inventory(test_df)
方案二:给装饰后的函数添加可修改属性,支持单独/批量修改
这种方法更灵活,既可以统一修改所有函数的样本量,也能单独调整某个函数的阈值:
步骤1:修改utils.py的装饰器
给装饰后的函数添加一个可直接修改的min_sample属性:
from functools import wraps def minSample(sample=None): def decorator(method): @wraps(method) def f(*args, **kwargs): # 每次调用读取函数自身的min_sample属性 if len(args[0]) < f.min_sample: return None return method(*args, **kwargs) # 给装饰后的函数绑定初始样本量 f.min_sample = sample return f return decorator
步骤2:pymetrics.py保持原有代码不变
from .utils import minSample MIN_SIZE = 10 @minSample(MIN_SIZE) def inventory(df): '''for quantisation''' return len(df)
生产代码中修改样本量
- 单独修改某个函数:
import pymetrics as mtr # 只修改inventory函数的最小样本量 mtr.inventory.min_sample = 0
- 批量修改所有被装饰的函数:
import pymetrics as mtr # 遍历模块内所有对象,找到有min_sample属性的函数并修改 for name, func in vars(mtr).items(): if callable(func) and hasattr(func, 'min_sample'): func.min_sample = 0
两种方案对比
- 方案一适合需要全局统一控制所有函数样本量的场景,修改一次就能生效所有函数;
- 方案二更灵活,允许对不同函数设置不同的阈值,也支持批量统一修改。
内容的提问来源于stack exchange,提问作者Philipp_Kats
相关产品推荐
相关产品推荐

