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

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

当前回答

这一变化扁平化嵌套字典,压缩键与max_level和自定义减速器。

  def flatten(d, max_level=None, reducer='tuple'):
      if reducer == 'tuple':
          reducer_seed = tuple()
          reducer_func = lambda x, y: (*x, y)
      else:
          raise ValueError(f'Unknown reducer: {reducer}')

      def impl(d, pref, level):
        return reduce(
            lambda new_d, kv:
                (max_level is None or level < max_level)
                and isinstance(kv[1], dict)
                and {**new_d, **impl(kv[1], reducer_func(pref, kv[0]), level + 1)}
                or {**new_d, reducer_func(pref, kv[0]): kv[1]},
                d.items(),
            {}
        )

      return impl(d, reducer_seed, 0)

其他回答

这里有一个使用堆栈的解决方案。没有递归。

def flatten_nested_dict(nested):
    stack = list(nested.items())
    ans = {}
    while stack:
        key, val = stack.pop()
        if isinstance(val, dict):
            for sub_key, sub_val in val.items():
                stack.append((f"{key}_{sub_key}", sub_val))
        else:
            ans[key] = val
    return ans

这一变化扁平化嵌套字典,压缩键与max_level和自定义减速器。

  def flatten(d, max_level=None, reducer='tuple'):
      if reducer == 'tuple':
          reducer_seed = tuple()
          reducer_func = lambda x, y: (*x, y)
      else:
          raise ValueError(f'Unknown reducer: {reducer}')

      def impl(d, pref, level):
        return reduce(
            lambda new_d, kv:
                (max_level is None or level < max_level)
                and isinstance(kv[1], dict)
                and {**new_d, **impl(kv[1], reducer_func(pref, kv[0]), level + 1)}
                or {**new_d, reducer_func(pref, kv[0]): kv[1]},
                d.items(),
            {}
        )

      return impl(d, reducer_seed, 0)

如果你是python语句的粉丝:

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

list(pd.json_normalize(my_dict).T.to_dict().values())[0]

返回:

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

如果你有一个字典列表,而不仅仅是一个字典,你可以从末尾保留[0]。

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

代码:

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

在Python3.5中提供功能和性能的解决方案如何?

from functools import reduce


def _reducer(items, key, val, pref):
    if isinstance(val, dict):
        return {**items, **flatten(val, pref + key)}
    else:
        return {**items, pref + key: val}

def flatten(d, pref=''):
    return(reduce(
        lambda new_d, kv: _reducer(new_d, *kv, pref), 
        d.items(), 
        {}
    ))

这是更有表现力的:

def flatten(d, pref=''):
    return(reduce(
        lambda new_d, kv: \
            isinstance(kv[1], dict) and \
            {**new_d, **flatten(kv[1], pref + kv[0])} or \
            {**new_d, pref + kv[0]: kv[1]}, 
        d.items(), 
        {}
    ))

在使用:

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

print(flatten(my_obj)) 
# {'d': [1, 2, 3], 'cby': 10, 'cbx': 5, 'ca': 2, 'a': 1}