[英]How to iterate through an N-level nested dictionary in Python?
我發現自己制作多級詞典相當多。 我總是不得不編寫非常冗長的代碼來遍歷包含大量臨時變量的字典的所有級別。
有沒有辦法概括這個函數來迭代多個級別而不是硬編碼並手動指定有多少級別?
def iterate_multilevel_dictionary(d, number_of_levels):
# How to auto-detect number of levels?
# number_of_levels = 0
if number_of_levels == 1:
for k1, v1 in d.items():
yield k1, v1
if number_of_levels == 2:
for k1, v1 in d.items():
for k2, v2 in v1.items():
yield k1, k2, v2
if number_of_levels == 3:
for k1, v1 in d.items():
for k2, v2 in v1.items():
for k3, v3 in v2.items():
yield k1, k2, k3, v3
# Level 1
d_level1 = {"a":1,"b":2,"c":3}
for items in iterate_multilevel_dictionary(d_level1, number_of_levels=1):
print(items)
# ('a', 1)
# ('b', 2)
# ('c', 3)
# Level 2
d_level2 = {"group_1":{"a":1}, "group_2":{"b":2,"c":3}}
for items in iterate_multilevel_dictionary(d_level2, number_of_levels=2):
print(items)
#('group_1', 'a', 1)
#('group_2', 'b', 2)
#('group_2', 'c', 3)
# Level 3
d_level3 = {"collection_1":d_level2}
for items in iterate_multilevel_dictionary(d_level3, number_of_levels=3):
print(items)
# ('collection_1', 'group_1', 'a', 1)
# ('collection_1', 'group_2', 'b', 2)
# ('collection_1', 'group_2', 'c', 3)
我是在看到@VoNWooDSoN 的回答后寫的。 我把它變成了一個迭代器,而不是在函數內部打印,並進行了一些更改以使其更具可讀性。 所以在這里看到他的原始答案。
def flatten(d, base=()):
for k, v in d.items():
if isinstance(v, dict):
yield from flatten(v, base + (k,))
else:
yield base + (k, v)
1-產量而不是印刷。
2- isinstance()
而不是type
以便dict
子類也可以工作。 您還可以使用來自typing
模塊的MutableMapping
而不是dict
使其更通用。
3- IMO,從.items()
獲取(k, v)
.items()
k
和d[k]
更具可讀性。
您是否想將其擴展為更通用的CAN (不必像 OP 中的解決方案那樣)接受depths
數以防萬一?
考慮以下示例:
d_level1 = {"a": 1, "b": 2, "c": 3}
d_level2 = {"group_1": {"a": 1}, "group_2": {"b": 2, "c": 3}}
d_level3 = {"collection_1": d_level2}
for items in flatten(d_level3):
print(items)
print('------------------------------')
for items in flatten(d_level3, depth=0):
print(items)
print('------------------------------')
for items in flatten(d_level3, depth=1):
print(items)
print('------------------------------')
for items in flatten(d_level3, depth=2):
print(items)
輸出:
('collection_1', 'group_1', 'a', 1)
('collection_1', 'group_2', 'b', 2)
('collection_1', 'group_2', 'c', 3)
------------------------------
('collection_1', {'group_1': {'a': 1}, 'group_2': {'b': 2, 'c': 3}})
------------------------------
('collection_1', 'group_1', {'a': 1})
('collection_1', 'group_2', {'b': 2, 'c': 3})
------------------------------
('collection_1', 'group_1', 'a', 1)
('collection_1', 'group_2', 'b', 2)
('collection_1', 'group_2', 'c', 3)
depth=None
不考慮深度(仍然像你想要的那樣工作)。 但是現在通過指定從0
到2
深度,您可以看到我們能夠迭代我們想要的深度。 這是代碼:
def flatten(d, base=(), depth=None):
for k, v in d.items():
if not isinstance(v, dict):
yield base + (k, v)
else:
if depth is None:
yield from flatten(v, base + (k,))
else:
if depth == 0:
yield base + (k, v)
else:
yield from flatten(v, base + (k,), depth - 1)
這是一個快速而骯臟的解決方案:
d_level1 = {"a":1,"b":2,"c":3}
d_level2 = {"group_1":{"a":1}, "group_2":{"b":2,"c":3}}
d_level3 = {"collection_1":d_level2}
def flatten(d_in, base=()):
for k in d_in:
if type(d_in[k]) == dict:
flatten(d_in[k], base+(k,))
else:
print(base + (k, d_in[k]))
flatten(d_level1)
# ('a', 1)
# ('b', 2)
# ('c', 3)
flatten(d_level2)
#('group_1', 'a', 1)
#('group_2', 'b', 2)
#('group_2', 'c', 3)
flatten(d_level3)
# ('collection_1', 'group_1', 'a', 1)
# ('collection_1', 'group_2', 'b', 2)
# ('collection_1', 'group_2', 'c', 3)
意識到!! Python 的遞歸限制約為 1000! 因此,在 python 中使用遞歸時,請仔細考慮您要嘗試做什么,並准備好在調用這樣的遞歸函數時捕獲 RuntimeError。
編輯:通過評論,我意識到我犯了一個錯誤,我沒有將密鑰添加到 level1 dict 輸出中,並且我使用的是可變結構作為默認參數。 我在打印聲明中添加了這些和括號並重新發布。 輸出現在與 OP 所需的輸出相匹配,並使用更好的現代 Python。
試試這個代碼
它還支持級別的組合
from typing import List, Tuple
def iterate_multilevel_dictionary(d: dict):
dicts_to_iterate: List[Tuple[dict, list]] = [(d, [])]
'''
the first item is the dict object and the second object is the prefix keys
'''
while dicts_to_iterate:
current_dict, suffix = dicts_to_iterate.pop()
for k, v in current_dict.items():
if isinstance(v, dict):
dicts_to_iterate.append((v, suffix + [k]))
else:
yield suffix + [k] + [v]
if __name__ == '__main__':
d_level1 = {"a": 1, "b": 2, "c": 3}
print(f"test for {d_level1}")
for items in iterate_multilevel_dictionary(d_level1):
print(items)
d_level2 = {"group_1": {"a": 1}, "group_2": {"b": 2, "c": 3}}
print(f"test for {d_level2}")
for items in iterate_multilevel_dictionary(d_level2):
print(items)
d_level3 = {"collection_1": d_level2}
print(f"test for {d_level3}")
for items in iterate_multilevel_dictionary(d_level3):
print(items)
d_level123 = {}
[d_level123.update(i) for i in [d_level1, d_level2, d_level3]]
print(f"test for {d_level123}")
for items in iterate_multilevel_dictionary(d_level123):
print(items)
輸出是:
test for {'a': 1, 'b': 2, 'c': 3}
['a', 1]
['b', 2]
['c', 3]
test for {'group_1': {'a': 1}, 'group_2': {'b': 2, 'c': 3}}
['group_2', 'b', 2]
['group_2', 'c', 3]
['group_1', 'a', 1]
test for {'collection_1': {'group_1': {'a': 1}, 'group_2': {'b': 2, 'c': 3}}}
['collection_1', 'group_2', 'b', 2]
['collection_1', 'group_2', 'c', 3]
['collection_1', 'group_1', 'a', 1]
test for {'a': 1, 'b': 2, 'c': 3, 'group_1': {'a': 1}, 'group_2': {'b': 2, 'c': 3}, 'collection_1': {'group_1': {'a': 1}, 'group_2': {'b': 2, 'c': 3}}}
['a', 1]
['b', 2]
['c', 3]
['collection_1', 'group_2', 'b', 2]
['collection_1', 'group_2', 'c', 3]
['collection_1', 'group_1', 'a', 1]
['group_2', 'b', 2]
['group_2', 'c', 3]
['group_1', 'a', 1]
使用遞歸是另一種方法,但我認為沒有遞歸的寫作更具挑戰性和效率:)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.