假设你有一本这样的字典:

{'a': 1,
 'c': {'a': 2,
       'b': {'x': 5,
             'y' : 10}},
 'd': [1, 2, 3]}

你会如何把它平摊成这样:

{'a': 1,
 'c_a': 2,
 'c_b_x': 5,
 'c_b_y': 10,
 'd': [1, 2, 3]}

当前回答

上面的答案真的很管用。我只是想加上我写的unflatten函数:

def unflatten(d):
    ud = {}
    for k, v in d.items():
        context = ud
        for sub_key in k.split('_')[:-1]:
            if sub_key not in context:
                context[sub_key] = {}
            context = context[sub_key]
        context[k.split('_')[-1]] = v
    return ud

注意:这并没有解释键中已经存在的'_',就像扁平化的对应物一样。

其他回答

使用flatdict库:

dic={'a': 1,
 'c': {'a': 2,
       'b': {'x': 5,
             'y' : 10}},
 'd': [1, 2, 3]}

import flatdict
f =  flatdict.FlatDict(dic,delimiter='_')
print(f)
#output
{'a': 1, 'c_a': 2, 'c_b_x': 5, 'c_b_y': 10, 'd': [1, 2, 3]}

如果你不介意递归函数,这里有一个解决方案。我还冒昧地包含了一个排除参数,以防您希望维护一个或多个值。

代码:

def flatten_dict(dictionary, exclude = [], delimiter ='_'):
    flat_dict = dict()
    for key, value in dictionary.items():
        if isinstance(value, dict) and key not in exclude:
            flatten_value_dict = flatten_dict(value, exclude, delimiter)
            for k, v in flatten_value_dict.items():
                flat_dict[f"{key}{delimiter}{k}"] = v
        else:
            flat_dict[key] = value
    return flat_dict

用法:

d = {'a':1, 'b':[1, 2], 'c':3, 'd':{'a':4, 'b':{'a':7, 'b':8}, 'c':6}, 'e':{'a':1,'b':2}}
flat_d = flatten_dict(dictionary=d, exclude=['e'], delimiter='.')
print(flat_d)

输出:

{'a': 1, 'b': [1, 2], 'c': 3, 'd.a': 4, 'd.b.a': 7, 'd.b.b': 8, 'd.c': 6, 'e': {'a': 1, 'b': 2}}

简单的函数来平嵌套字典。对于Python 3,用.items()替换.iteritems()

def flatten_dict(init_dict):
    res_dict = {}
    if type(init_dict) is not dict:
        return res_dict

    for k, v in init_dict.iteritems():
        if type(v) == dict:
            res_dict.update(flatten_dict(v))
        else:
            res_dict[k] = v

    return res_dict

这个想法/要求是: 获取不保留父键的平面字典。

用法示例:

dd = {'a': 3, 
      'b': {'c': 4, 'd': 5}, 
      'e': {'f': 
                 {'g': 1, 'h': 2}
           }, 
      'i': 9,
     }

flatten_dict(dd)

>> {'a': 3, 'c': 4, 'd': 5, 'g': 1, 'h': 2, 'i': 9}

保留父密钥也很简单。

这与imran和ralu的回答相似。它不使用生成器,而是使用闭包的递归:

def flatten_dict(d, separator='_'):
  final = {}
  def _flatten_dict(obj, parent_keys=[]):
    for k, v in obj.iteritems():
      if isinstance(v, dict):
        _flatten_dict(v, parent_keys + [k])
      else:
        key = separator.join(parent_keys + [k])
        final[key] = v
  _flatten_dict(d)
  return final

>>> print flatten_dict({'a': 1, 'c': {'a': 2, 'b': {'x': 5, 'y' : 10}}, 'd': [1, 2, 3]})
{'a': 1, 'c_a': 2, 'c_b_x': 5, 'd': [1, 2, 3], 'c_b_y': 10}

利用递归,保持简单和人类可读:

def flatten_dict(dictionary, accumulator=None, parent_key=None, separator="."):
    if accumulator is None:
        accumulator = {}

    for k, v in dictionary.items():
        k = f"{parent_key}{separator}{k}" if parent_key else k
        if isinstance(v, dict):
            flatten_dict(dictionary=v, accumulator=accumulator, parent_key=k)
            continue

        accumulator[k] = v

    return accumulator

调用很简单:

new_dict = flatten_dict(dictionary)

or

new_dict = flatten_dict(dictionary, separator="_")

如果我们想改变默认分隔符。

稍微分解一下:

当函数第一次被调用时,它只被调用传递我们想要扁平化的字典。这里的累加器参数支持递归,稍后我们将看到。因此,我们将accumulator实例化到一个空字典中,我们将在其中放入原始字典中的所有嵌套值。

if accumulator is None:
    accumulator = {}

当我们遍历字典的值时,我们为每个值构造一个键。对于第一次调用,parent_key参数将为None,而对于每个嵌套字典,它将包含指向它的键,因此我们将该键前置。

k = f"{parent_key}{separator}{k}" if parent_key else k

如果键k指向的值v是一个字典,函数调用自身,传递嵌套的字典、累加器(通过引用传递,因此对它的所有更改都是在同一个实例上完成的)和键k,这样我们就可以构造连接键。注意continue语句。我们想要跳过if语句块之外的下一行,这样嵌套的字典就不会在键k下的累加器中结束。

if isinstance(v, dict):
    flatten_dict(dict=v, accumulator=accumulator, parent_key=k)
    continue

那么,如果值v不是字典,我们该怎么办呢?把它原封不动地放在累加器里。

accumulator[k] = v

一旦完成,我们只返回累加器,原始的字典参数保持不变。

NOTE

这只适用于有字符串作为键的字典。它将与实现__repr__方法的哈希对象一起工作,但将产生不想要的结果。