Spyder 3.2.7变量浏览器中自定义字典类DateScheme的数据类型变更问题
我之前也在Spyder老版本里遇到过自定义dict子类在变量浏览器里被识别成普通dict的问题,结合你的代码来看,问题主要出在两点:一是你重写了__setattr__把所有属性(包括类本身的name)都塞进了dict的键值对里,导致Spyder的变量浏览器没法区分这是你的自定义类还是原生dict;二是Spyder 3.2.7对内置类型子类的类型检测逻辑比较简单,容易把子类当成父类显示。
下面给你几个可行的解决方案,按推荐程度排序:
方案1:区分实例属性与字典存储项(最小改动)
你的__setattr__现在会把所有属性都存在dict里,这会让对象的__dict__几乎是空的,Spyder识别的时候就会优先把它当成普通dict。我们可以修改__setattr__,把属于类本身的核心属性(比如name)保留在实例的__dict__中,其他属性再存入字典:
class DateScheme(dict): """ 日期方案容器对象 可将键作为属性访问的类字典对象 """ def __init__(self, name): # 直接用super().__setattr__避免触发重写的__setattr__ super().__setattr__('name', name) # 省略其他键初始化逻辑 def __dir__(self): # 把实例属性和字典键都加入补全列表 return list(self.keys()) + ['name'] def __setattr__(self, key, value): # 定义哪些属性属于实例本身,不存入字典 if key == 'name' or key.startswith('_'): super().__setattr__(key, value) else: self[key] = value def __getattr__(self, key): try: return self[key] except KeyError: # 尝试从实例属性中获取(比如name) try: return super().__getattribute__(key) except AttributeError: raise AttributeError(key)
这样修改后,name会被存在实例的__dict__里,Spyder的变量浏览器就能识别出这是DateScheme类的对象,而不是普通dict,同时你依然可以用obj.name或者obj['key']的方式访问数据。
方案2:使用组合而非继承(更稳定)
如果方案1还是解决不了问题,建议放弃直接继承dict,改用组合模式——内部维护一个字典来存储数据,这样完全避免了Spyder把你的类当成原生dict的问题:
class DateScheme: """ 日期方案容器对象 可将键作为属性访问的类字典对象 """ def __init__(self, name): self.name = name self._inner_dict = {} # 用内部字典存储业务数据 def __dir__(self): # 把内部字典的键和实例属性都加入自动补全列表 return list(self._inner_dict.keys()) + ['name', '_inner_dict'] def __setattr__(self, key, value): # 区分实例属性和业务数据键 if key in ['name', '_inner_dict']: super().__setattr__(key, value) else: self._inner_dict[key] = value def __getattr__(self, key): try: return self._inner_dict[key] except KeyError: raise AttributeError(f"'DateScheme' object has no attribute '{key}'") # 实现字典的核心方法,保持字典式访问能力 def __getitem__(self, key): return self._inner_dict[key] def __setitem__(self, key, value): self._inner_dict[key] = value def keys(self): return self._inner_dict.keys() def items(self): return self._inner_dict.items() def values(self): return self._inner_dict.values()
这种方式的好处是完全脱离了原生dict的类型绑定,Spyder的变量浏览器会明确显示这是DateScheme类型的对象,同时你依然拥有和之前一样的属性访问、字典访问能力。
方案3:添加Spyder专属的自定义表示
Spyder的变量浏览器支持一个特殊的方法__spyder_repr__,你可以给类添加这个方法,强制变量浏览器显示自定义的标识,明确这是你的DateScheme对象:
class DateScheme(dict): # 保留你原有的__init__、__dir__、__setattr__、__getattr__方法 # 添加Spyder专属表示方法 def __spyder_repr__(self): return f"DateScheme(name='{self.name}', keys={list(self.keys())})"
这个方法会让变量浏览器显示你自定义的字符串,即使它依然识别成dict类型,你也能一眼区分出这是你的自定义对象。
内容的提问来源于stack exchange,提问作者Westfalenmats

