1 回答

TA貢獻1921條經驗 獲得超9個贊
這是一組遞歸生成器,可用于搜索由字典和列表組成的對象。find_key產生一個元組,其中包含字典鍵列表和導致您傳入的鍵的列表索引;元組還包含與該鍵關聯的值。因為它是一個生成器,所以如果需要的話,如果對象包含多個匹配鍵,它將找到所有匹配鍵。
def find_key(obj, key):
if isinstance(obj, dict):
yield from iter_dict(obj, key, [])
elif isinstance(obj, list):
yield from iter_list(obj, key, [])
def iter_dict(d, key, indices):
for k, v in d.items():
if k == key:
yield indices + [k], v
if isinstance(v, dict):
yield from iter_dict(v, key, indices + [k])
elif isinstance(v, list):
yield from iter_list(v, key, indices + [k])
def iter_list(seq, key, indices):
for k, v in enumerate(seq):
if isinstance(v, dict):
yield from iter_dict(v, key, indices + [k])
elif isinstance(v, list):
yield from iter_list(v, key, indices + [k])
# test
data = {
'1_data': {
'4_data': [
{'5_data': 'hooray'},
{'3_data': 'hooray2'}
],
'2_data': []
}
}
for t in find_key(data, '3_data'):
print(t)
輸出
(['1_data', '4_data', 1, '3_data'], 'hooray2')
要獲取單個鍵列表,可以傳遞find_key給該next函數。如果要使用鍵列表來獲取關聯的值,則可以使用簡單的for循環。
seq, val = next(find_key(data, '3_data'))
print('seq:', seq, 'val:', val)
obj = data
for k in seq:
obj = obj[k]
print('obj:', obj, obj == val)
輸出
seq: ['1_data', '4_data', 1, '3_data'] val: hooray2
obj: hooray2 True
如果密鑰可能丟失,請提供next適當的默認元組。例如:
seq, val = next(find_key(data, '6_data'), ([], None))
print('seq:', seq, 'val:', val)
if seq:
obj = data
for k in seq:
obj = obj[k]
print('obj:', obj, obj == val)
輸出
seq: [] val: None
請注意,此代碼是針對Python 3的。要在Python 2上運行,您需要替換所有yield from語句,例如replace
yield from iter_dict(obj, key, [])
與
for u in iter_dict(obj, key, []):
yield u
怎么運行的
要了解此代碼的工作原理,您需要熟悉遞歸和Python 生成器。您可能還會發現此頁面有幫助:了解Python中的生成器;在線上也有各種Python生成器教程。
json.load或返回的Python對象json.loads通常是字典,但也可以是列表。我們將該對象find_key與objarg一起傳遞給生成器,作為key我們想要定位的字符串。find_key然后根據需要調用iter_dict或iter_list,將它們,對象,鍵和一個空列表傳遞給它們,該空列表indices用于收集dict鍵和列出指向所需鍵的索引。
iter_dict在其ddict arg 的頂層迭代每個(k,v)對。如果k與我們要查找的鍵匹配,則會生成當前indices列表k并附加到當前列表以及相關的值。因為iter_dict是遞歸的,所以產生的(索引列表,值)對將傳遞到遞歸的上一個級別,最終使它們到達find_key并到達調用的代碼find_key。請注意,這是遞歸的“基本情況”:這是確定此遞歸路徑是否指向所需鍵的代碼的一部分。如果遞歸路徑找不到與我們要查找的鍵匹配的鍵,則該遞歸路徑將不會添加任何內容,indices并且它將終止而不會產生任何結果。
如果當前v是一個字典,那么我們需要檢查它包含的所有(鍵,值)對。我們通過對進行遞歸調用來實現iter_dict,將v其作為起始對象和當前indices列表進行傳遞。如果當前v是一個列表,我們改為調用iter_list,將相同的參數傳遞給它。
iter_listiter_dict除了列表不包含任何鍵,它只包含值外,其工作方式與之類似。因此,我們不執行k == key測試,而是遞歸到原始列表包含的所有字典或列表。
該過程的最終結果是,當我們進行迭代時,find_key我們獲得(索引,值)對,其中每個indices列表是dict鍵的序列和列表索引,這些鍵成功地終止于帶有所需鍵的dict項中,并且value是關聯的值用那個特定的鑰匙。
如果您想查看此代碼的其他示例,請參閱如何修改嵌套Json的鍵以及如何從python的字典中選擇深度嵌套的key:values。
還要看看我的新的,更簡化的show_indices功能。
分享編輯
添加回答
舉報