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

{'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]}

当前回答

使用生成器的Python 3.3解决方案:

def flattenit(pyobj, keystring=''):
   if type(pyobj) is dict:
     if (type(pyobj) is dict):
         keystring = keystring + "_" if keystring else keystring
         for k in pyobj:
             yield from flattenit(pyobj[k], keystring + k)
     elif (type(pyobj) is list):
         for lelm in pyobj:
             yield from flatten(lelm, keystring)
   else:
      yield keystring, pyobj

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

#your flattened dictionary object
flattened={k:v for k,v in flattenit(my_obj)}
print(flattened)

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

其他回答

这并不局限于字典,而是实现.items()的每个映射类型。进一步列表更快,因为它避免了if条件。尽管如此,功劳还是归于伊姆兰:

def flatten(d, parent_key=''):
    items = []
    for k, v in d.items():
        try:
            items.extend(flatten(v, '%s%s_' % (parent_key, k)).items())
        except AttributeError:
            items.append(('%s%s' % (parent_key, k), v))
    return dict(items)

我正在考虑UserDict的一个子类来自动神奇地平键。

class FlatDict(UserDict):
    def __init__(self, *args, separator='.', **kwargs):
        self.separator = separator
        super().__init__(*args, **kwargs)

    def __setitem__(self, key, value):
        if isinstance(value, dict):
            for k1, v1 in FlatDict(value, separator=self.separator).items():
                super().__setitem__(f"{key}{self.separator}{k1}", v1)
        else:
            super().__setitem__(key, value)

‌ 优点是键可以动态添加,或者使用标准字典实例化,毫无疑问:

>>> fd = FlatDict(
...    {
...        'person': {
...            'sexe': 'male', 
...            'name': {
...                'first': 'jacques',
...                'last': 'dupond'
...            }
...        }
...    }
... )
>>> fd
{'person.sexe': 'male', 'person.name.first': 'jacques', 'person.name.last': 'dupond'}
>>> fd['person'] = {'name': {'nickname': 'Bob'}}
>>> fd
{'person.sexe': 'male', 'person.name.first': 'jacques', 'person.name.last': 'dupond', 'person.name.nickname': 'Bob'}
>>> fd['person.name'] = {'civility': 'Dr'}
>>> fd
{'person.sexe': 'male', 'person.name.first': 'jacques', 'person.name.last': 'dupond', 'person.name.nickname': 'Bob', 'person.name.civility': 'Dr'}

这与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}

使用dict.popitem()在直接的嵌套列表类递归中:

def flatten(d):
    if d == {}:
        return d
    else:
        k,v = d.popitem()
        if (dict != type(v)):
            return {k:v, **flatten(d)}
        else:
            flat_kv = flatten(v)
            for k1 in list(flat_kv.keys()):
                flat_kv[k + '_' + k1] = flat_kv[k1]
                del flat_kv[k1]
            return {**flat_kv, **flatten(d)}

这是一种“功能性的”、“单行程序”实现。它是递归的,基于条件表达式和字典理解。

def flatten_dict(dd, separator='_', prefix=''):
    return { prefix + separator + k if prefix else k : v
             for kk, vv in dd.items()
             for k, v in flatten_dict(vv, separator, kk).items()
             } if isinstance(dd, dict) else { prefix : dd }

测试:

In [2]: flatten_dict({'abc':123, 'hgf':{'gh':432, 'yu':433}, 'gfd':902, 'xzxzxz':{"432":{'0b0b0b':231}, "43234":1321}}, '.')
Out[2]: 
{'abc': 123,
 'gfd': 902,
 'hgf.gh': 432,
 'hgf.yu': 433,
 'xzxzxz.432.0b0b0b': 231,
 'xzxzxz.43234': 1321}