Предположим, что у вас есть словарь:
{'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]}
Предположим, что у вас есть словарь:
{'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]}
В принципе так же, как вы сглаживаете вложенный список, вам просто нужно выполнить дополнительную работу для итерации 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}
Есть два больших соображения, которые должен учитывать исходный плакат:
{'a_b':{'c':1}, 'a':{'b_c':2}}
приведет к {'a_b_c':???}
. Решение ниже устраняет проблему, возвращая итерабельность пар.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 уровня, хотя один из ответов на этой странице дает общее решение.)
Или, если вы уже используете панд, вы можете сделать это с помощью 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]}
Вот своего рода "функциональная", "однострочная" реализация. Он рекурсивный и основан на условном выражении и понимании 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}
Если вы используете 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
(без _
)
код:
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.
Это не ограничивается словарями, но каждым типом отображения, который реализует .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)
Как насчет функционального и производительного решения в 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}
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}
Простая функция для выравнивания вложенных словарей. Для 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}
Хранить родительские ключи также просто.
Это похоже на оба ответа 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 также содержит списки 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)
Ответы выше работают очень хорошо. Просто подумал, что я добавлю незакрытую функцию, которую я написал:
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
Примечание. Это не учитывает "_", уже присутствующий в клавишах, подобно сглаживающим аналогам.
Вот алгоритм для элегантной замены на месте. Протестировано с 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
.
Если вы хотите создать вложенный словарь и получить список всех уникальных ключей, то вот решение:
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))
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
Использование генераторов:
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}
Использование 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)}
Я попытался сгладить следующий 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"
}
Я всегда предпочитаю доступ к объектам 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
Я на самом деле недавно написал пакет под названием 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 использовать параллельную обработку для ускорения процесса.
Просто используйте 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='_')