You need to enable JavaScript to run this app. 支持属性访问嵌套字典的自定义Map类print输出与__dict__值不一致问题
优惠活动
大模型
产品
解决方案
定价
更多

支持属性访问嵌套字典的自定义Map类print输出与__dict__值不一致问题

问题根因

你自定义的Map类同时维护了两套独立的存储,二者没有同步导致数据不一致:

  • 父类dict的原生存储:打印data实例时输出的是这部分的内容
  • 实例自身的__dict__存储:你通过data.df访问到的是这部分的内容

具体到你的代码逻辑,__setitem__方法执行时,先把原始的普通字典存入父类dict,再把转换后的Map实例存入__dict__,二者从赋值开始就是完全独立的两个对象。后续你修改data.df[time]时,改的是__dict__里的Map实例,父类dict里存储的原始普通字典完全没有被修改,因此打印实例时看到的还是旧值。

另外你的__getattr__方法存在逻辑错误:先调用super(Map, self).__getattr__(attr),但父类dict本身没有实现__getattr__方法,属性不存在时会直接抛出AttributeError,根本不会执行后续的return self.get(attr)逻辑。

自定义类修复方案

调整__setitem__的执行顺序,保证父类dict和__dict__中存储的是同一个对象,同时修复__getattr__的错误逻辑即可:

class Map(dict):
    """
    支持点号访问的字典实现
    Example:
    m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
    """
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.items():
                    self[k] = v

        if kwargs:
            for k, v in kwargs.items():
                self[k] = v

    def __getattr__(self, attr):
        try:
            return self[attr]
        except KeyError:
            raise AttributeError(f"'Map' object has no attribute '{attr}'")

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        # 先转换字典为Map实例,保证两处存储的是同一个对象
        if isinstance(value, dict):
            value = Map(value)
        super(Map, self).__setitem__(key, value)
        self.__dict__[key] = value

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(Map, self).__delitem__(key)
        del self.__dict__[key]

修复后你原来的测试代码可以得到预期输出。

更推荐的现成数据结构

不需要自己造轮子,有多个成熟的现成方案适配你的需求:

  • 标准库SimpleNamespace:轻量无依赖,适合属性固定的简单场景
    from types import SimpleNamespace
    # 嵌套也支持点号访问
    data = SimpleNamespace(
        start_date = "2020-01-01",
        finish_date = "2021-01-01",
        name = "Type A",
        df = SimpleNamespace(yearly = 0, monthly = 0, weekly = 0)
    )
    # 直接用data.df.yearly访问和修改
    
  • dataclass:Python 3.7+内置,支持自定义方法、默认值等,比SimpleNamespace灵活性更高
    from dataclasses import dataclass
    
    @dataclass
    class DfData:
        yearly: int
        monthly: int
        weekly: int
    
    @dataclass
    class ProjectData:
        start_date: str
        finish_date: str
        name: str
        df: DfData
    
    data = ProjectData(
        start_date = "2020-01-01",
        finish_date = "2021-01-01",
        name = "Type A",
        df = DfData(yearly=0, monthly=0, weekly=0)
    )
    
  • Pydantic BaseModel:适合工业级项目,支持类型校验、自动序列化、数据转换等功能,即使存储pd.DataFrame也可以通过自定义字段类型支持
    from pydantic import BaseModel
    
    class DfData(BaseModel):
        yearly: int
        monthly: int
        weekly: int
    
    class ProjectData(BaseModel):
        start_date: str
        finish_date: str
        name: str
        df: DfData
    
    data = ProjectData(
        start_date = "2020-01-01",
        finish_date = "2021-01-01",
        name = "Type A",
        df = {"yearly":0, "monthly":0, "weekly":0}
    )
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.18 08:24:01