【问题标题】:Accessing python dict using nested key lookup string使用嵌套键查找字符串访问 python dict
【发布时间】:2025-11-30 02:10:01
【问题描述】:

我希望在 python 中创建一个简单的嵌套“查找”机制,并希望确保在 python 的庞大库中隐藏的某个地方在创建它之前还没有这样做。

我正在寻找一个像这样格式化的字典

my_dict = { 
  "root": { 
    "secondary": { 
      "user1": { 
          "name": "jim", 
          "age": 24 
      }, 
      "user2": { 
        "name": "fred", 
        "age": 25 
      } 
    } 
  } 
}

我正在尝试使用类似于

的十进制表示法来访问数据
root.secondary.user2

并将生成的 dict 作为响应返回。我在想一定有什么东西可以做到这一点,我可以毫不费力地写一个,但我想确保我没有重新创建文档中可能缺少的东西。谢谢

【问题讨论】:

  • 您是在寻找语法糖 (mydict.root.secondary.user2) 还是只是通过字符串查找?

标签: python dictionary


【解决方案1】:

标准库中没有为此目的,但自己编写代码相当容易:

>>> key = "root.secondary.user2"
>>> reduce(dict.get, key.split("."), my_dict)
{'age': 25, 'name': 'fred'}

这利用了这样一个事实,即在字典 d 中查找键 k 可以写为 dict.get(d, k)。使用reduce() 迭代地应用它会导致所需的结果。

编辑:为了完整起见,使用此方法获取、设置或删除字典键的三个函数:

def get_key(my_dict, key):
    return reduce(dict.get, key.split("."), my_dict)

def set_key(my_dict, key, value):
    key = key.split(".")
    my_dict = reduce(dict.get, key[:-1], my_dict)
    my_dict[key[-1]] = value

def del_key(my_dict, key):
    key = key.split(".")
    my_dict = reduce(dict.get, key[:-1], my_dict)
    del my_dict[key[-1]]

【讨论】:

  • 这对于检索非常有用,但是关于使用相同方法实际更新该字典中的字段的任何想法
  • +1 表示reduce() 答案;虽然我也很喜欢这个答案,但希望我能 +2。
  • @Tanerax:要更新,拆分键的最后一个组件并使用前导组件检索包含字典。在答案中添加了示例代码。
  • 如果key 不存在,get_key 会发生什么?
  • @Randy,它会引发 KeyError。
【解决方案2】:

你可以拥有它。您可以使用类似于下面的代码将 dict 子类化,添加键查找(甚至保留名称 dict)。但是{...} 表单仍将使用内置的 dict 类(现在称为 orig_dict),因此您必须将其括起来,如下所示:Dict({...})。此实现递归地将字典转换为新形式,因此您不必对任何本身是普通字典的字典条目使用上述方法。

orig_dict = dict
class Dict(orig_dict):
    def __init__(self, *args, **kwargs):
        super(Dict, self).__init__(*args, **kwargs)
        for k, v in self.iteritems():
            if type(v) == orig_dict and not isinstance(v, Dict):
                super(Dict, self).__setitem__(k, Dict(v))
    def __getattribute__(self, k):
        try: return super(Dict, self).__getattribute__(k)
        except: return self.__getitem__(k)
    def __setattr__(self, k, v):
        if self.has_key(k): self.__setitem__(k, v)
        else: return super(Dict, self).__setattr__(k, v)
    def __delattr__(self, k):
        try: self.__delitem__(k)
        except: super(Dict, self).__delattr__(k)
    def __setitem__(self, k, v):
        toconvert = type(v) == orig_dict and not isinstance(v, Dict)
        super(Dict, self).__setitem__(k, Dict(v) if toconvert else v)

# dict = Dict  <-- you can even do this but I advise against it

# testing:
b = Dict(a=1, b=Dict(c=2, d=3))
c = Dict({'a': 1, 'b': {'c': 2, 'd': 3}})
d = Dict(a=1, b={'c': 2, 'd': {'e': 3, 'f': {'g': 4}}})

b.a = b.b
b.b = 1
d.b.d.f.g = 40
del d.b.d.e
d.b.c += d.b.d.f.g
c.b.c += c.a
del c.a
print b
print c
print d

【讨论】:

    【解决方案3】:

    递归仍然有效。

    def walk_into( dict, key ):
        head, _, tail = key.partition('.')
        if tail:
            return walk_into( dict[head], tail )
        return dict, key
    d, k = walk_into( my_dict, "root.secondary.user2" )
    

    d[k] 可用于获取或放置新值。

    【讨论】:

      【解决方案4】:

      我对这个和其他一些东西有一个非常完整的实现here。存储库heretrict.utiltrict.trict 中的__get__ 方法相结合,如果您不想安装它,可能会有您需要的东西。而且它实际上是在 conda-forge 中,即使 README 可能会说如果我在你阅读本文之前没有及时更新它。

      【讨论】: