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

如何实现赋值自动嵌套建字典、取值抛KeyError的类字典结构

这是一个很有意思的需求——既要支持链式赋值时自动补全嵌套字典,又要在直接访问不存在的键时抛出KeyError,而不是返回默认值。下面是一个可行的实现思路和代码:

实现思路

核心问题确实是Python无法直接区分__getitem__是用于赋值链还是单纯取值。我们可以通过代理对象来解决这个问题:当访问不存在的键时,不直接创建字典,而是返回一个代理对象。只有当这个代理对象被赋值时(触发它的__setitem__方法),我们才会在原字典中递归创建嵌套结构;如果用户直接使用这个代理对象(比如打印、判断存在性),则抛出KeyError。

完整代码实现
from collections import UserDict

class _NestedAssignmentProxy:
    def __init__(self, root_dict, key_path):
        self.root_dict = root_dict
        self.key_path = key_path  # 记录当前的键路径,比如['level_1', 'level_2']

    def __getitem__(self, key):
        # 链式访问时,返回新的代理对象,扩展键路径
        return _NestedAssignmentProxy(self.root_dict, self.key_path + [key])

    def __setitem__(self, key, value):
        # 当代理对象被赋值时,递归创建嵌套字典
        current = self.root_dict.data
        for k in self.key_path:
            if k not in current:
                current[k] = {}
            else:
                # 如果中间键对应的值不是字典,覆盖为字典(和你之前的实现逻辑一致)
                if not isinstance(current[k], (dict, TestDict)):
                    current[k] = {}
            current = current[k]
        # 设置最终的键值对
        current[key] = value

    def _get_full_key(self):
        # 格式化键路径,方便在错误信息中展示
        return "['" + "']['".join(self.key_path) + "']"

    # 当代理对象被直接访问时(比如打印、取值判断),抛出KeyError
    def __repr__(self):
        raise KeyError(f"{self._get_full_key()}")
    
    def __str__(self):
        raise KeyError(f"{self._get_full_key()}")
    
    def __bool__(self):
        raise KeyError(f"{self._get_full_key()}")
    
    def __len__(self):
        raise KeyError(f"{self._get_full_key()}")

class TestDict(UserDict):
    def __getitem__(self, key):
        if key in self.data:
            return self.data[key]
        # 访问不存在的键时,返回代理对象
        return _NestedAssignmentProxy(self, [key])
    
    def __repr__(self):
        # 保持和普通字典一致的输出格式
        return repr(self.data)
测试验证
# 测试链式赋值
test_dict = TestDict()
test_dict['level_1']['level_2']['level_3'] = 'Hello'
print(test_dict)
# 输出: {'level_1': {'level_2': {'level_3': 'Hello'}}}

# 测试中间键非字典的情况
test_dict2 = TestDict()
test_dict2['level_1'] = 'not a dict'
test_dict2['level_1']['level_2'] = 'Hello'
print(test_dict2)
# 输出: {'level_1': {'level_2': 'Hello'}}

# 测试访问不存在的键
test_dict['unknown_key']
# 抛出: KeyError: ['unknown_key']

test_dict['level_1']['unknown']
# 抛出: KeyError: ['level_1']['unknown']
方案说明
  • 代理对象的作用:它相当于一个“占位符”,只有在被赋值时才会触发实际的字典创建逻辑,完美区分了赋值和取值场景。
  • 错误提示友好:代理对象会记录完整的键路径,抛出的KeyError会显示具体的不存在的键路径,方便调试。
  • 兼容原有逻辑:保留了你之前实现中“覆盖非字典中间键为字典”的行为,确保逻辑一致性。

这个实现完全支持你想要的链式赋值语法,同时严格遵守“访问不存在的键抛错”的要求,比传入键列表的写法更符合Python的使用习惯。

内容的提问来源于stack exchange,提问作者Rasmus Fisker Bang

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 10:22:42