Python中非内置类型实例如何实现不可变?numpy元类赋值报错解析
实现非内置类型实例的不可变,常用以下几种方式:
重写
__setattr__方法,阻止属性修改
在类中自定义__setattr__,当尝试修改已存在的属性时抛出异常,仅允许初始化阶段设置属性:class ImmutableObj: def __init__(self, value): self._value = value def __setattr__(self, name, value): # 若属性已存在,拒绝修改 if hasattr(self, name): raise AttributeError("无法修改不可变对象的属性") # 初始化阶段允许设置属性 super().__setattr__(name, value) @property def value(self): return self._value结合
__slots__与只读属性
用__slots__限制实例可拥有的属性范围,再通过property只提供属性的读取方法,不暴露修改接口:class ImmutableObj: __slots__ = ('_value',) def __init__(self, value): self._value = value @property def value(self): return self._value冻结实例属性字典
初始化完成后,用types.MappingProxyType包装实例的__dict__,将其转为只读映射:import types class ImmutableObj: def __init__(self, value): self._value = value # 冻结属性字典,禁止后续修改 self.__dict__ = types.MappingProxyType(self.__dict__)
执行type(np.dtype).moo = 7时报错,核心原因是numpy的_DTypeMeta元类被标记为不可变类型,具体细节如下:
Python底层的类型控制机制
Python的C API允许给类型对象设置Py_TPFLAGS_IMMUTABLETYPE标志位,当类型被标记为这个状态后,type.__setattr__在尝试修改类型属性时会触发检查,一旦检测到该标志,就抛出TypeError,阻止属性修改。numpy的实现方式
numpy._DTypeMeta是用C扩展实现的元类,numpy在定义这个元类时,通过C API设置了Py_TPFLAGS_IMMUTABLETYPE标志。这和float等内置不可变类型的原理完全一致——并非依赖内存位置检查,而是通过类型对象的标志位来限制修改。关于
type.__setattr__的检测逻辑
当执行type.__setattr__(numpy._DTypeMeta, "moo", 7)时,底层会先检查目标类型对象的标志位,发现_DTypeMeta被标记为不可变,就直接抛出错误,和它是否是内置库无关。
内容的提问来源于stack exchange,提问作者to7m

