Python中自动为时序类生成属性访问方法的问题及报错排查
问题分析与解决方案
错误原因
核心问题是元类中生成property时的闭包延迟绑定陷阱:
循环中定义的所有lambda共享同一个循环变量a的引用,而非定义时的a值。当current_xxx属性被调用时,循环早已结束,a的值会变成最后一个遍历的注解字段(在CBTimeseries中就是current_t)。此时getattr(self, a)返回的是int类型的current_t,尝试下标访问自然触发TypeError: 'int' object is not subscriptable。
另外,CBTimeseries中重复定义current_t: int = 0会和基类Timeseries的__post_init__逻辑冲突,进一步加剧问题。
修复代码
1. 修改元类,解决闭包绑定问题
class TimeseriesMeta(type): def __new__(mcs, name, bases, attrs, *args, **kwargs): annotations = attrs.get('__annotations__', {}) for a in annotations: if a in ('t_max', 'current_t'): continue # 通过默认参数捕获当前循环的a值,避免闭包延迟绑定 attrs[f'current_{a}'] = property(lambda self, attr=a: getattr(self, attr)[self.current_t]) attrs[f'prev_{a}'] = property(lambda self, attr=a: getattr(self, attr)[self.current_t - 1]) return super().__new__(mcs, name, bases, attrs, *args, **kwargs)
2. 修正子类定义,移除重复字段
移除CBTimeseries中重复定义的current_t,基类已通过__post_init__完成初始化:
@dataclass class CBTimeseries(Timeseries): # ea prefixes indicate Euro Area ea_inflation: np.ndarray ea_gdp: np.ndarray ea_growth: np.ndarray profits: np.ndarray r_policy_rate: np.ndarray shadow_interest_rate: np.ndarray equity: np.ndarray row_debt: np.ndarray t_max: int @classmethod def init_default(cls, t_max: int, initial_ea_inflation: float, initial_ea_gdp: float, initial_rate: float, initial_row_debt: float, initial_equity: float = 1e7): ea_inflation = np.zeros(t_max) ea_inflation[0] = initial_ea_inflation ea_gdp = np.zeros(t_max) ea_gdp[0] = initial_ea_gdp ea_growth = np.zeros(t_max) profits = np.zeros(t_max) r_policy_rate = np.zeros(t_max) r_policy_rate[0] = initial_rate shadow_interest_rate = np.zeros(t_max) equity = np.zeros(t_max) equity[0] = initial_equity row_debt = np.zeros(t_max) row_debt[0] = initial_row_debt return cls(ea_inflation=ea_inflation, ea_gdp=ea_gdp, ea_growth=ea_growth, profits=profits, r_policy_rate=r_policy_rate, shadow_interest_rate=shadow_interest_rate, equity=equity, row_debt=row_debt, t_max=t_max)
原理说明
- 闭包绑定修复:给
lambda添加默认参数attr=a,会在lambda定义时就将当前循环的a值绑定到attr上,每个property对应的lambda都会持有独立的属性名称,不会再共享循环变量的引用。 - 字段去重:移除子类的
current_t字段,避免dataclass自动生成的初始化逻辑覆盖基类__post_init__的设置,保证current_t始终是预期的时序索引。
修改后重新运行测试,current_ea_inflation等属性将正确返回对应时序数组的指定下标值,报错问题解决。
内容的提问来源于stack exchange,提问作者Someone1348
相关产品推荐
相关产品推荐

