Сгладить вложенные словари, сжав ключи

Предположим, что у вас есть словарь:

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

Ответ 1

В принципе так же, как вы сглаживаете вложенный список, вам просто нужно выполнить дополнительную работу для итерации dict по ключу/значению, создания новых ключей для вашего нового словаря и создания словаря на последнем этапе.

import collections

def flatten(d, parent_key='', sep='_'):
    items = []
    for k, v in d.items():
        new_key = parent_key + sep + k if parent_key else k
        if isinstance(v, collections.MutableMapping):
            items.extend(flatten(v, new_key, sep=sep).items())
        else:
            items.append((new_key, v))
    return dict(items)

>>> flatten({'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}

Ответ 2

Есть два больших соображения, которые должен учитывать исходный плакат:

  • Есть ли проблемы с клеточным пространством? Например, {'a_b':{'c':1}, 'a':{'b_c':2}} приведет к {'a_b_c':???}. Решение ниже устраняет проблему, возвращая итерабельность пар.
  • Если производительность является проблемой, функция key-reducer (которую я называю здесь "join" ) требует доступа ко всему ключевому пути или может просто выполнять O (1) на каждом node в дереве? Если вы хотите сказать joinedKey = '_'.join(*keys), это будет стоить вам время O (N ^ 2). Однако, если вы хотите сказать nextKey = previousKey+'_'+thisKey, это даст вам время O (N). Решение, приведенное ниже, позволяет вам выполнять оба действия (поскольку вы можете просто конкатенировать все ключи, а затем выполнять их постпроцесс).

(Производительность вряд ли будет проблемой, но я расскажу о втором вопросе в случае, если кто-то еще заботится: при реализации этого существует множество опасных вариантов. Если вы сделаете это рекурсивно, уступите и снова уступите или что-нибудь еще эквивалент, который касается узлов более одного раза (что довольно легко сделать случайно), вы делаете потенциально O (N ^ 2) работу, а не O (N). Это потому, что, возможно, вы вычисляете ключ a, затем a_1 then a_1_i..., а затем вычисляя a, затем a_1, затем a_1_ii..., но на самом деле вам не придется снова вычислять a_1. Даже если вы не пересчитываете его, повторное получение этого (подход "по уровню" ) столь же плох. Хорошим примером является представление о производительности на {1:{1:{1:{1:...(N times)...{1:SOME_LARGE_DICTIONARY_OF_SIZE_N}...}}}})

Ниже приведена функция, которую я написал flattenDict(d, join=..., lift=...), которая может быть адаптирована ко многим целям и может делать то, что вы хотите. К сожалению, довольно сложно сделать ленивую версию этой функции, не прибегая к вышеуказанным штрафам за производительность (многие встроенные python, такие как chain.from_iterable, фактически не эффективны, что я понял только после обширного тестирования трех разных версий этого кода, прежде чем опираться на этот).

from collections import Mapping
from itertools import chain
from operator import add

_FLAG_FIRST = object()

def flattenDict(d, join=add, lift=lambda x:x):
    results = []
    def visit(subdict, results, partialKey):
        for k,v in subdict.items():
            newKey = lift(k) if partialKey==_FLAG_FIRST else join(partialKey,lift(k))
            if isinstance(v,Mapping):
                visit(v, results, newKey)
            else:
                results.append((newKey,v))
    visit(d, results, _FLAG_FIRST)
    return results

Чтобы лучше понять, что происходит, ниже приведена диаграмма для тех, кто не знаком с reduce (слева), иначе известный как "fold left". Иногда он рисуется с начальным значением вместо k0 (не является частью списка, переданным в функцию). Здесь J является нашей функцией join. Предварительно обрабатываем каждый k n с помощью lift(k).

               [k0,k1,...,kN].foldleft(J)
                           /    \
                         ...    kN
                         /
       J(k0,J(k1,J(k2,k3)))
                       /  \
                      /    \
           J(J(k0,k1),k2)   k3
                    /   \
                   /     \
             J(k0,k1)    k2
                 /  \
                /    \
               k0     k1

Это фактически то же самое, что и functools.reduce, но где наша функция делает это ко всем ключевым путям дерева.

>>> reduce(lambda a,b:(a,b), range(5))
((((0, 1), 2), 3), 4)

Демонстрация (которая в противном случае была бы введена в docstring):

>>> testData = {
        'a':1,
        'b':2,
        'c':{
            'aa':11,
            'bb':22,
            'cc':{
                'aaa':111
            }
        }
    }
from pprint import pprint as pp

>>> pp(dict( flattenDict(testData, lift=lambda x:(x,)) ))
{('a',): 1,
 ('b',): 2,
 ('c', 'aa'): 11,
 ('c', 'bb'): 22,
 ('c', 'cc', 'aaa'): 111}

>>> pp(dict( flattenDict(testData, join=lambda a,b:a+'_'+b) ))
{'a': 1, 'b': 2, 'c_aa': 11, 'c_bb': 22, 'c_cc_aaa': 111}    

>>> pp(dict( (v,k) for k,v in flattenDict(testData, lift=hash, join=lambda a,b:hash((a,b))) ))
{1: 12416037344,
 2: 12544037731,
 11: 5470935132935744593,
 22: 4885734186131977315,
 111: 3461911260025554326}

Производительность:

from functools import reduce
def makeEvilDict(n):
    return reduce(lambda acc,x:{x:acc}, [{i:0 for i in range(n)}]+range(n))

import timeit
def time(runnable):
    t0 = timeit.default_timer()
    _ = runnable()
    t1 = timeit.default_timer()
    print('took {:.2f} seconds'.format(t1-t0))

>>> pp(makeEvilDict(8))
{7: {6: {5: {4: {3: {2: {1: {0: {0: 0,
                                 1: 0,
                                 2: 0,
                                 3: 0,
                                 4: 0,
                                 5: 0,
                                 6: 0,
                                 7: 0}}}}}}}}}

import sys
sys.setrecursionlimit(1000000)

forget = lambda a,b:''

>>> time(lambda: dict(flattenDict(makeEvilDict(10000), join=forget)) )
took 0.10 seconds
>>> time(lambda: dict(flattenDict(makeEvilDict(100000), join=forget)) )
[1]    12569 segmentation fault  python

... вздох, не думай, что это моя вина...


[несущественная историческая заметка из-за проблем модерации]

Что касается предполагаемого дубликата Сгладить словарь словарей (2 уровня в глубину) списков в Python:

Это решение вопроса может быть реализовано в терминах этого путем выполнения sorted( sum(flatten(...),[]) ). Обратное невозможно: хотя верно, что значения of flatten(...) могут быть восстановлены из предполагаемого дубликата путем сопоставления аккумулятора более высокого порядка, невозможно восстановить ключи. (edit: Также выясняется, что вопрос о предполагаемом дубликате владельца полностью отличается тем, что он имеет дело только со словарями ровно на 2 уровня, хотя один из ответов на этой странице дает общее решение.)

Ответ 3

Или, если вы уже используете панд, вы можете сделать это с помощью json_normalize() следующим образом:

import pandas as pd

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

df = pd.io.json.json_normalize(d, sep='_')

print(df.to_dict(orient='records')[0])

Выход:

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

Ответ 4

Вот своего рода "функциональная", "однострочная" реализация. Он рекурсивный и основан на условном выражении и понимании dict.

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}

Ответ 5

Если вы используете pandas, в pandas.io.json._normalize 1 есть скрытая функция, которая называется nested_to_record, которая делает это точно.

from pandas.io.json._normalize import nested_to_record    

flat = nested_to_record(my_dict, sep='_')

1 В версиях для панд 0.24.x и более старых версиях pandas.io.json.normalize (без _)

Ответ 6

код:

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

def parse_dict(init, lkey=''):
    ret = {}
    for rkey,val in init.items():
        key = lkey+rkey
        if isinstance(val, dict):
            ret.update(parse_dict(val, key+'_'))
        else:
            ret[key] = val
    return ret

print(parse_dict(test,''))

Результаты:

$ python test.py
{'a': 1, 'c_a': 2, 'c_b_x': 5, 'd': [1, 2, 3], 'c_b_y': 10}

Я использую python3.2, обновление для вашей версии python.

Ответ 7

Это не ограничивается словарями, но каждым типом отображения, который реализует .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)

Ответ 8

Как насчет функционального и производительного решения в 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}

Ответ 9

My Python 3.3 Solution с использованием генераторов:

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}

Ответ 10

Простая функция для выравнивания вложенных словарей. Для Python 3 замените .iteritems() на .items()

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}

Хранить родительские ключи также просто.

Ответ 11

Это похоже на оба ответа 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}

Ответ 12

Решение Давуда очень приятно, но не дает удовлетворительных результатов, когда вложенный dict также содержит списки dicts, но его код может быть адаптирован для этого случая:

def flatten_dict(d):
    items = []
    for k, v in d.items():
        try:
            if (type(v)==type([])): 
                for l in v: items.extend(flatten_dict(l).items())
            else: 
                items.extend(flatten_dict(v).items())
        except AttributeError:
            items.append((k, v))
    return dict(items)

Ответ 13

Ответы выше работают очень хорошо. Просто подумал, что я добавлю незакрытую функцию, которую я написал:

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

Примечание. Это не учитывает "_", уже присутствующий в клавишах, подобно сглаживающим аналогам.

Ответ 14

Вот алгоритм для элегантной замены на месте. Протестировано с Python 2.7 и Python 3.5. Использование символа точки в качестве разделителя.

def flatten_json(json):
    if type(json) == dict:
        for k, v in list(json.items()):
            if type(v) == dict:
                flatten_json(v)
                json.pop(k)
                for k2, v2 in v.items():
                    json[k+"."+k2] = v2

Пример:

d = {'a': {'b': 'c'}}                   
flatten_json(d)
print(d)
unflatten_json(d)
print(d)

Вывод:

{'a.b': 'c'}
{'a': {'b': 'c'}}

Я опубликовал этот код здесь вместе с соответствующей функцией unflatten_json.

Ответ 15

Если вы хотите создать вложенный словарь и получить список всех уникальных ключей, то вот решение:

def flat_dict_return_unique_key(data, unique_keys=set()):
    if isinstance(data, dict):
        [unique_keys.add(i) for i in data.keys()]
        for each_v in data.values():
            if isinstance(each_v, dict):
                flat_dict_return_unique_key(each_v, unique_keys)
    return list(set(unique_keys))

Ответ 16

def flatten(unflattened_dict, separator='_'):
    flattened_dict = {}

    for k, v in unflattened_dict.items():
        if isinstance(v, dict):
            sub_flattened_dict = flatten(v, separator)
            for k2, v2 in sub_flattened_dict.items():
                flattened_dict[k + separator + k2] = v2
        else:
            flattened_dict[k] = v

    return flattened_dict

Ответ 17

Использование генераторов:

def flat_dic_helper(prepand,d):
    if len(prepand) > 0:
        prepand = prepand + "_"
    for k in d:
        i=d[k]
        if type(i).__name__=='dict':
            r = flat_dic_helper(prepand+k,i)
            for j in r:
                yield j
        else:
            yield (prepand+k,i)

def flat_dic(d): return dict(flat_dic_helper("",d))

d={'a': 1, 'c': {'a': 2, 'b': {'x': 5, 'y' : 10}}, 'd': [1, 2, 3]}
print(flat_dic(d))


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

Ответ 18

Использование 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)}

Ответ 19

Я попытался сгладить следующий json:

{
    "blkio_stats": {
        "io_merged_recursive": [],
        "io_queue_recursive": [],
        "io_service_bytes_recursive": [],
        "io_service_time_recursive": [],
        "io_serviced_recursive": [],
        "io_time_recursive": [],
        "io_wait_time_recursive": [],
        "sectors_recursive": []
    },
    "cpu_stats": {
        "cpu_usage": {
            "percpu_usage": [56477793484, 53845065668],
            "total_usage": 110322859152,
            "usage_in_kernelmode": 74310000000,
            "usage_in_usermode": 24760000000
        },
        "system_cpu_usage": 11583455420000000,
        "throttling_data": {
            "periods": 0,
            "throttled_periods": 0,
            "throttled_time": 0
        }
    },
    "memory_stats": {
        "failcnt": 0,
        "limit": 4074237952,
        "max_usage": 485281792,
        "stats": {
            "active_anon": 7823360,
            "active_file": 8192,
            "cache": 8192,
            "dirty": 0,
            "hierarchical_memory_limit": 9223372036854771712,
            "inactive_anon": 0,
            "inactive_file": 0,
            "mapped_file": 0,
            "pgfault": 5602509,
            "pgmajfault": 0,
            "pgpgin": 5421031,
            "pgpgout": 5429850,
            "rss": 7823360,
            "rss_huge": 6291456,
            "total_active_anon": 7823360,
            "total_active_file": 8192,
            "total_cache": 8192,
            "total_dirty": 0,
            "total_inactive_anon": 0,
            "total_inactive_file": 0,
            "total_mapped_file": 0,
            "total_pgfault": 5602509,
            "total_pgmajfault": 0,
            "total_pgpgin": 5421031,
            "total_pgpgout": 5429850,
            "total_rss": 7823360,
            "total_rss_huge": 6291456,
            "total_unevictable": 0,
            "total_writeback": 0,
            "unevictable": 0,
            "writeback": 0
        },
        "usage": 7831552
    },
    "networks": {
        "eth0": {
            "rx_bytes": 1512143070,
            "rx_dropped": 0,
            "rx_errors": 0,
            "rx_packets": 2605267,
            "tx_bytes": 1450105558,
            "tx_dropped": 0,
            "tx_errors": 0,
            "tx_packets": 2326940
        }
    },
    "pids_stats": {},
    "precpu_stats": {
        "cpu_usage": {
            "percpu_usage": null,
            "total_usage": 0,
            "usage_in_kernelmode": 0,
            "usage_in_usermode": 0
        },
        "system_cpu_usage": 0,
        "throttling_data": {
            "periods": 0,
            "throttled_periods": 0,
            "throttled_time": 0
        }
    },
    "read": "2017-02-17T10:22:07.278903338Z"
}

Лучший вариант сгладить этот файл с помощью. поскольку разделительный знак был следующим: fooobar.com/questions/63934/... Он оставил только "pids_stats": {}, option:

{
    "blkio_stats.io_merged_recursive": [],
    "blkio_stats.io_queue_recursive": [],
    "blkio_stats.io_service_bytes_recursive": [],
    "blkio_stats.io_service_time_recursive": [],
    "blkio_stats.io_serviced_recursive": [],
    "blkio_stats.io_time_recursive": [],
    "blkio_stats.io_wait_time_recursive": [],
    "blkio_stats.sectors_recursive": [],
    "cpu_stats.cpu_usage.percpu_usage": [56477793484, 53845065668],
    "cpu_stats.cpu_usage.total_usage": 110322859152,
    "cpu_stats.cpu_usage.usage_in_kernelmode": 74310000000,
    "cpu_stats.cpu_usage.usage_in_usermode": 24760000000,
    "cpu_stats.system_cpu_usage": 11583455420000000,
    "cpu_stats.throttling_data.periods": 0,
    "cpu_stats.throttling_data.throttled_periods": 0,
    "cpu_stats.throttling_data.throttled_time": 0,
    "memory_stats.failcnt": 0,
    "memory_stats.limit": 4074237952,
    "memory_stats.max_usage": 485281792,
    "memory_stats.stats.active_anon": 7823360,
    "memory_stats.stats.active_file": 8192,
    "memory_stats.stats.cache": 8192,
    "memory_stats.stats.dirty": 0,
    "memory_stats.stats.hierarchical_memory_limit": 9223372036854771712,
    "memory_stats.stats.inactive_anon": 0,
    "memory_stats.stats.inactive_file": 0,
    "memory_stats.stats.mapped_file": 0,
    "memory_stats.stats.pgfault": 5602509,
    "memory_stats.stats.pgmajfault": 0,
    "memory_stats.stats.pgpgin": 5421031,
    "memory_stats.stats.pgpgout": 5429850,
    "memory_stats.stats.rss": 7823360,
    "memory_stats.stats.rss_huge": 6291456,
    "memory_stats.stats.total_active_anon": 7823360,
    "memory_stats.stats.total_active_file": 8192,
    "memory_stats.stats.total_cache": 8192,
    "memory_stats.stats.total_dirty": 0,
    "memory_stats.stats.total_inactive_anon": 0,
    "memory_stats.stats.total_inactive_file": 0,
    "memory_stats.stats.total_mapped_file": 0,
    "memory_stats.stats.total_pgfault": 5602509,
    "memory_stats.stats.total_pgmajfault": 0,
    "memory_stats.stats.total_pgpgin": 5421031,
    "memory_stats.stats.total_pgpgout": 5429850,
    "memory_stats.stats.total_rss": 7823360,
    "memory_stats.stats.total_rss_huge": 6291456,
    "memory_stats.stats.total_unevictable": 0,
    "memory_stats.stats.total_writeback": 0,
    "memory_stats.stats.unevictable": 0,
    "memory_stats.stats.writeback": 0,
    "memory_stats.usage": 7831552,
    "networks.eth0.rx_bytes": 1512143070,
    "networks.eth0.rx_dropped": 0,
    "networks.eth0.rx_errors": 0,
    "networks.eth0.rx_packets": 2605267,
    "networks.eth0.tx_bytes": 1450105558,
    "networks.eth0.tx_dropped": 0,
    "networks.eth0.tx_errors": 0,
    "networks.eth0.tx_packets": 2326940,
    "precpu_stats.cpu_usage.percpu_usage": null,
    "precpu_stats.cpu_usage.total_usage": 0,
    "precpu_stats.cpu_usage.usage_in_kernelmode": 0,
    "precpu_stats.cpu_usage.usage_in_usermode": 0,
    "precpu_stats.system_cpu_usage": 0,
    "precpu_stats.throttling_data.periods": 0,
    "precpu_stats.throttling_data.throttled_periods": 0,
    "precpu_stats.throttling_data.throttled_time": 0,
    "read": "2017-02-17T10:22:07.278903338Z"
}

С точки зрения unflattening файла, с. как вариант, я выбираю Сгладить вложенные словари Python, сжимая ключи - потому что он работал рекурсивно и отлично. Обратите внимание, что "pids_stats": {} отсутствует, поскольку он был потерян на предыдущем шаге.

{
    "blkio_stats": {
        "io_merged_recursive": [],
        "io_queue_recursive": [],
        "io_service_bytes_recursive": [],
        "io_service_time_recursive": [],
        "io_serviced_recursive": [],
        "io_time_recursive": [],
        "io_wait_time_recursive": [],
        "sectors_recursive": []
    },
    "cpu_stats": {
        "cpu_usage": {
            "percpu_usage": [56477793484, 53845065668],
            "total_usage": 110322859152,
            "usage_in_kernelmode": 74310000000,
            "usage_in_usermode": 24760000000
        },
        "system_cpu_usage": 11583455420000000,
        "throttling_data": {
            "periods": 0,
            "throttled_periods": 0,
            "throttled_time": 0
        }
    },
    "memory_stats": {
        "failcnt": 0,
        "limit": 4074237952,
        "max_usage": 485281792,
        "stats": {
            "active_anon": 7823360,
            "active_file": 8192,
            "cache": 8192,
            "dirty": 0,
            "hierarchical_memory_limit": 9223372036854771712,
            "inactive_anon": 0,
            "inactive_file": 0,
            "mapped_file": 0,
            "pgfault": 5602509,
            "pgmajfault": 0,
            "pgpgin": 5421031,
            "pgpgout": 5429850,
            "rss": 7823360,
            "rss_huge": 6291456,
            "total_active_anon": 7823360,
            "total_active_file": 8192,
            "total_cache": 8192,
            "total_dirty": 0,
            "total_inactive_anon": 0,
            "total_inactive_file": 0,
            "total_mapped_file": 0,
            "total_pgfault": 5602509,
            "total_pgmajfault": 0,
            "total_pgpgin": 5421031,
            "total_pgpgout": 5429850,
            "total_rss": 7823360,
            "total_rss_huge": 6291456,
            "total_unevictable": 0,
            "total_writeback": 0,
            "unevictable": 0,
            "writeback": 0
        },
        "usage": 7831552
    },
    "networks": {
        "eth0": {
            "rx_bytes": 1512143070,
            "rx_dropped": 0,
            "rx_errors": 0,
            "rx_packets": 2605267,
            "tx_bytes": 1450105558,
            "tx_dropped": 0,
            "tx_errors": 0,
            "tx_packets": 2326940
        }
    },
    "precpu_stats": {
        "cpu_usage": {
            "percpu_usage": null,
            "total_usage": 0,
            "usage_in_kernelmode": 0,
            "usage_in_usermode": 0
        },
        "system_cpu_usage": 0,
        "throttling_data": {
            "periods": 0,
            "throttled_periods": 0,
            "throttled_time": 0
        }
    },
    "read": "2017-02-17T10:22:07.278903338Z"
}

Ответ 20

Я всегда предпочитаю доступ к объектам dict через .items(), поэтому для сглаживания диктов я использую следующий рекурсивный генератор flat_items(d). Если вы хотите снова использовать dict, просто оберните его так: flat = dict(flat_items(d))

def flat_items(d, key_separator='.'):
    """
    Flattens the dictionary containing other dictionaries like here: https://stackoverflow.com/questions/6027558/flatten-nested-python-dictionaries-compressing-keys

    >>> example = {'a': 1, 'c': {'a': 2, 'b': {'x': 5, 'y' : 10}}, 'd': [1, 2, 3]}
    >>> flat = dict(flat_items(example, key_separator='_'))
    >>> assert flat['c_b_y'] == 10
    """
    for k, v in d.items():
        if type(v) is dict:
            for k1, v1 in flat_items(v, key_separator=key_separator):
                yield key_separator.join((k, k1)), v1
        else:
            yield k, v

Ответ 21

Я на самом деле недавно написал пакет под названием cherrypicker, чтобы разобраться с такими вещами, поскольку мне приходилось делать это так часто!

Я думаю, что следующий код даст вам именно то, что вы ищете:

from cherrypicker import CherryPicker

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

picker = CherryPicker(dct)
picker.flatten().get()

Вы можете установить пакет с помощью:

pip install cherrypicker

... а также другие документы и рекомендации на https://cherrypicker.readthedocs.io.

Другие методы могут быть быстрее, но приоритет этого пакета - сделать такие задачи быстрыми и легкими. Если у вас есть большой список объектов, которые нужно сгладить, вы также можете указать CherryPicker использовать параллельную обработку для ускорения процесса.

Ответ 22

Просто используйте python-benedict, это подкласс dict, который предлагает множество функций, включая метод flatten. Можно установить его с помощью pip: pip install python-benedict

https://github.com/fabiocaccamo/python-benedict#flatten

from benedict import benedict 

d = benedict(data)
f = d.flatten(separator='_')