使用__new__后传递字典至继承类的配置类bug排查咨询
踩坑经历:深度配置类的隐蔽赋值Bug追踪
刚花了整整一小时揪出一个藏得很深的bug,忍不住来分享下——我自己实现了一个深度配置类,重写了所有基础类型来做额外的校验和控制,比如执行config['heading']['value'] = 'xyz'时,会先跑一遍自定义验证逻辑再真正设置值。本来以为是偶发问题,结果发现它一直存在,只是平时表现太隐蔽没被察觉。
以下是我精简后的核心代码结构,保留了和bug相关的关键逻辑:
class ConfigNode: def __init__(self, data=None): self._data = data or {} # 初始化时自动包装所有基础类型 for key, value in self._data.items(): self._data[key] = self._wrap_basic_type(value) def __getitem__(self, key): # 访问节点时返回包装后的对象 value = self._data.get(key) if isinstance(value, dict): return ConfigNode(value) return value def __setitem__(self, key, value): # 赋值前先验证 if not self._validate(key, value): raise ValueError(f"Invalid value for key '{key}': {value}") # 这里就是bug的根源!直接存入了原生值,没有包装 self._data[key] = value def _wrap_basic_type(self, value): # 重写基础类型,添加额外控制 if isinstance(value, str): return StringConfig(value) elif isinstance(value, int): return IntConfig(value) # 其他基础类型的包装... return value def _validate(self, key, value): # 示例验证逻辑:heading的value必须是非空字符串 if key == 'value' and 'heading' in self._data: return isinstance(value, str) and len(value.strip()) > 0 return True # 重写的字符串类型示例 class StringConfig(str): def __setattr__(self, name, value): # 禁止直接修改属性,强制通过ConfigNode的赋值逻辑 raise AttributeError("Cannot modify StringConfig directly")
问题分析
bug的核心原因是:在__setitem__方法中,我直接把原生值存入了_data字典,没有经过_wrap_basic_type的包装。这就导致后续访问这个值时,拿到的是原生的str/int类型,而不是我们重写的控制类——某些场景下(比如直接读取值)不会有问题,但如果依赖重写类的额外逻辑(比如禁止直接修改属性),就会出现诡异的“偶发”错误,因为只有初始化时的数值是包装过的,后续赋值的都是原生类型。
修复方案
只需要修改__setitem__方法,把赋值的内容换成包装后的对象即可:
def __setitem__(self, key, value): if not self._validate(key, value): raise ValueError(f"Invalid value for key '{key}': {value}") # 先包装再存入 self._data[key] = self._wrap_basic_type(value)
这样不管是初始化还是后续赋值,所有的基础类型都会被正确包装,校验和控制逻辑就能一直生效了。
内容的提问来源于stack exchange,提问作者Peter
相关产品推荐
相关产品推荐

