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

如何实现兼具dataclass特性与字典功能的Python类?

解决方案

你可以通过两种方式实现需求:一种是利用__getattr__简化属性访问逻辑,另一种是通过元类自动为标注字段生成@property。两种方案都继承自dict,保留所有字典操作能力,同时支持通过类型标注快速定义可属性访问的字段。

方案一:利用__getattr__实现轻量版

这种方式无需额外元类,代码简洁,仅需为需要属性访问的字段添加类型标注即可:

class ApiDict(dict):
    def __getattr__(self, name):
        # 仅允许访问类标注过的字段作为属性
        if name in self.__class__.__annotations__:
            return self[name]
        raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")

# 定义API响应类
class Response(ApiDict):
    Value: str
    Status: int

# 测试使用
rr = Response({"Value": "success", "Status": 200, "UnusedField": "extra_data"})

# 支持属性访问标注字段
assert rr.Value == "success"
assert rr.Status == 200

# 支持字典方式访问任意字段(包括未标注的新增字段)
assert rr["UnusedField"] == "extra_data"
assert rr["Value"] == rr.Value

方案二:元类自动生成@property

如果希望完全模拟手动编写@property的效果,可以用元类自动为标注字段生成只读属性:

class ApiDictMeta(type):
    def __new__(cls, name, bases, namespace):
        # 遍历类的类型标注,为每个字段生成@property
        annotations = namespace.get('__annotations__', {})
        for field_name in annotations:
            def prop_getter(self, field=field_name):
                return self[field]
            namespace[field_name] = property(prop_getter)
        return super().__new__(cls, name, bases, namespace)

class ApiDict(dict, metaclass=ApiDictMeta):
    pass

# 定义API响应类
class Response(ApiDict):
    Value: str
    Status: int

# 测试使用
rr = Response({"Value": "test", "Status": 404, "NewField": "added_in_v2"})

assert rr.Value == "test"
assert rr["Status"] == 404
assert rr["NewField"] == "added_in_v2"

方案优势

  • 完全继承dict:所有字典操作(如keys()、items()、__getitem__)都能正常使用,新增的JSON字段无需修改代码即可通过[]访问。
  • 低维护成本:仅需为需要用属性访问的字段添加类型标注,无需维护所有JSON键,API版本更新时仅新增用到的标注即可。
  • 平滑迁移:现有代码中response["Value"]的写法无需改动,可逐步迁移到.Value的属性访问方式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 10:42:40