【问题标题】:recursive access to dictionary and modification递归访问字典和修改
【发布时间】:2014-05-25 12:34:12
【问题描述】:

我有以下字典:

my_dict = {'key1': {'key2': {'foo': 'bar'} } }

我想在 key1->key2->key3 中附加一个条目,其值为 'blah' yielding:

my_dict = {'key1': {'key2': {'foo': 'bar', 'key3': 'blah'} } }

我正在寻找一种与键数无关的通用解决方案,即 key1->key2->key3->key4->key5 也应该可以工作,即使 key3 向下的键不存在。所以我得到:

my_dict = {'key1': {'key2': {'foo': 'bar', 'key3': {'key4': {'key5': 'blah'} } } } }

提前致谢。

【问题讨论】:

  • 我的回答中的技术也适用于此;使用reduce() 走到最里面的字典(根据需要创建额外的字典)。
  • @MartijnPieters:感谢您的提示。但我认为这只是我问题的前半部分。现在,在创建键之后,我如何在给定键列表和值的情况下访问它?
  • target = reduce(lambda d, k: d.setdefault(k, {}), path[:-1], my_dict) 是正确字典的路径(因为path[-1] 只是实际值的列表键),然后target[path[-1]] = 'blah' 执行最后一步。
  • 使用reduce() 遍历对象并设置属性而不是字典和键的类似答案:How to use string formatting to dynamically assign variables

标签: python dictionary


【解决方案1】:

可以使用reduce() function遍历一系列嵌套字典:

def get_nested(d, path):
    return reduce(dict.__getitem__, path, d)

演示:

>>> def get_nested(d, path):
...     return reduce(dict.__getitem__, path, d)
... 
>>> my_dict = {'key1': {'key2': {'foo': 'bar', 'key3': {'key4': {'key5': 'blah'}}}}}
>>> get_nested(my_dict, ('key1', 'key2', 'key3', 'key4', 'key5'))
'blah'

此版本在密钥不存在时抛出异常:

>>> get_nested(my_dict, ('key1', 'nonesuch'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in get_nested
KeyError: 'nonesuch'

但您可以将 dict.__getitem__ 替换为 lambda d, k: d.setdefault(k, {}) 以使其创建空字典:

def get_nested_default(d, path):
    return reduce(lambda d, k: d.setdefault(k, {}), path, d)

演示:

>>> def get_nested_default(d, path):
...     return reduce(lambda d, k: d.setdefault(k, {}), path, d)
... 
>>> get_nested_default(my_dict, ('key1', 'nonesuch'))
{}
>>> my_dict
{'key1': {'key2': {'key3': {'key4': {'key5': 'blah'}}, 'foo': 'bar'}, 'nonesuch': {}}}

要在给定路径设置一个值,遍历除最后一个之外的所有键,然后在常规字典赋值中使用最后一个键:

def set_nested(d, path, value):
    get_nested_default(d, path[:-1])[path[-1]] = value

这使用get_nested_default() 函数根据需要添加空字典:

>>> def set_nested(d, path, value):
...     get_nested_default(d, path[:-1])[path[-1]] = value
... 
>>> my_dict = {'key1': {'key2': {'foo': 'bar'}}}
>>> set_nested(my_dict, ('key1', 'key2', 'key3', 'key4', 'key5'), 'blah')
>>> my_dict
{'key1': {'key2': {'key3': {'key4': {'key5': 'blah'}}, 'foo': 'bar'}}}

【讨论】:

    【解决方案2】:

    Martijn Pieters 出色答案的替代方法是使用嵌套的defaultdict,而不是常规字典:

    from collections import defaultdict
    
    nested = lambda: defaultdict(nested) # nested dictionary factory
    
    my_dict = nested()
    

    您可以使用常规的嵌套字典访问语义来设置值,并且将根据需要创建空字典来填充中间层:

    my_dict["key1"]["key2"]["key3"] = "blah"
    

    这当然需要在编写代码设置值时提前知道键的数量。如果您希望能够处理可变长度的键列表,而不是固定数字,您将需要函数来为您执行获取和设置,就像在 Martijn 的回答中一样。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-01-12
      • 2019-07-30
      • 2021-04-10
      • 2018-10-08
      • 1970-01-01
      • 2011-03-03
      • 1970-01-01
      相关资源
      最近更新 更多