You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python元类使用__setattr__修改类属性报dictproxy赋值错误原因求解

错误原因解析
  • 不管是Python 2还是Python 3,类对象的__dict__都是只读的代理对象(Python 2中叫dictproxy,Python 3中叫mappingproxy),设计上就不允许直接对该字典做项赋值操作,你在元类__setattr__中执行self.__dict__['_shelve'] = xxx、self.__dict__[name] = value的操作,直接触发了该代理对象的写入限制,所以抛出报错。
  • 此外还有一个隐含逻辑问题:你把foo、bar、_shelve这几个初始属性写在了元类的定义中,这些属性会变成元类本身的属性,而非你创建的DATA类的属性,后续访问DATA.foo实际是在查找元类的属性,不符合你的设计预期。
修正后可用的实现方案

Python 2 版本

import shelve

class _DATA_MetaClass(type):
    def __init__(cls, name, bases, attrs):
        # 初始属性直接绑定到目标类
        cls.foo = {}
        cls.bar = {}
        cls._shelve = None
    
    def __setattr__(cls, name, value):
        if cls._shelve is None:
            # 调用父类type的原生__setattr__修改类属性,避免直接操作只读的dictproxy
            super(_DATA_MetaClass, cls).__setattr__('_shelve', shelve.open('datastorage.shelve'))
        # 写入shelve完成持久化
        cls._shelve[name] = value
        # 修改类属性
        super(_DATA_MetaClass, cls).__setattr__(name, value)

class DATA(object):
    __metaclass__ = _DATA_MetaClass

# 测试
DATA.x = 'val'
print(DATA.x)
print(DATA._shelve['x'])

Python 3 版本

import shelve

class _DATA_MetaClass(type):
    def __init__(cls, name, bases, attrs):
        cls.foo = {}
        cls.bar = {}
        cls._shelve = None
    
    def __setattr__(cls, name, value):
        if cls._shelve is None:
            super().__setattr__('_shelve', shelve.open('datastorage.shelve'))
        cls._shelve[name] = value
        super().__setattr__(name, value)

class DATA(metaclass=_DATA_MetaClass):
    pass

# 测试
DATA.x = 'val'
print(DATA.x)
print(DATA._shelve['x'])

注意:程序退出前请主动调用DATA._shelve.close()关闭shelve文件,避免数据丢失。

内容的提问来源于stack exchange,提问作者tim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 14:39:03