最近寫接口測試程序中,需要查詢多層嵌套字典中某個Key的值,查到則返回,查不到返回None,查詢的Key可以是任意層次的Key,如 Value本身也是多層字典,或者Key已經是葉子結點。
思想:利用Python的遞歸思想,逐層深入遍歷,最后返回結果值
最終的成品代碼參考了一下博客內容:

1 #獲取字典中的objkey對應的值,適用於字典嵌套 2 #targetDict:要查找的字典;serchKey:要查找的目標key 3 #ret:遞歸過程中,向外部(上層)傳送 return值。被查找目標在第幾層,則返回幾次ret 4 #default:查不到符合的serchKey,就返回默認值None 5 def dict_getValue(targetDict,serchKey,default=None): 6 for k,v in targetDict.items(): 7 if k == serchKey: 8 return v 9 elif isinstance(v,dict): 10 ret = dict_getValue(v,serchKey,default) 11 if ret is not default: #ret與default=None不等,表示找到serchKey,則ret會作為返回值向上層返回。 12 return ret 13 return default
測試數據,拼接在上面的代碼里即可

1 if __name__ == '__main__': 2 targetDict ={"H": {"Ver": ["aaaa","bbbb"],"ACID": {'kkk':"aaaaa"},"CInf": [100,2,1000]}, 3 "B": { "Login": {"Type": "LP","Name": "41SS676","Pwd": {'aaa':"123456"},"ForToken": 1}}} 4 print (recursionSearch(targetDict,'Name'))
在成品之前,嘗試過幾種寫法,都無法達到最終要求,進行了一些分析,現記錄下來:
1、查找的Key只能是葉子結點,非葉子結點的無法實現查找,代碼如下:

1 def recursionSearch(targetDict,serchKey): #遞歸查找 2 for k,v in targetDict.items(): 3 if isinstance(v,dict) : #值是字典元素,則遞歸處理 4 recursionSearch(v,serchKey) 5 elif k == serchKey: 6 pp=targetDict[k] 7 print (pp) 8 return pp
結果:
print (recursionSearch(targetDict,'kkk')) 看到打印出來的葉子結點的值正是我想要查找的Key='kkk'的值‘aaaaaa’,
print (recursionSearch(targetDict,'Name')) 換第二個分支里面的葉子結點,也能看到函數內打印結果41SS676是我想要的,應該是對的吧?
Python輸出:
aaaaaa
None <---函數輸出結果
41SS676
None <---函數輸出結果
[Finished in 0.2s]
但為毛函數結果是None呢???
分析:
(1).代碼確實能查找葉子結點,但。。。函數返回還是有問題。
(2).這代碼遇到值為字典型的就會繼續深入,如果目標Key的值恰好是字典數據,程序只會繼續深入而不會就此停止。
2、更換if條件,不會直接到葉子結點級別才開始查找

1 def recursionSearch(targetDict,serchKey): #遞歸查找 2 for k,v in targetDict.items(): 3 if k == serchKey: 4 return v 5 elif isinstance(v,dict) : #值是字典元素,則遞歸處理 6 recursionSearch(v,serchKey)
分析:
(2).這個程序最后一行只進行了遞歸調用,但是沒有返回遞歸的值,導致一旦出現遞歸,則必然返回斷檔,結果必然是None。無return的函數返回值就是None,Python規定。
參考《Python學習手冊第4版》531頁 “沒有renturn語句的函數”
3、那就把遞歸調用的返回值也return一下

1 def recursionSearch(targetDict,serchKey): #遞歸查找 2 for k,v in targetDict.items(): 3 if k == serchKey: 4 return v 5 elif isinstance(v,dict) : #值是字典元素,則遞歸處理 6 ret = recursionSearch(v,serchKey) 7 return ret
結果:這種代碼只能按照第一個元素這條線深入遞歸下去,無論最終找到或者找不到目標值,都會結束遞歸。
這種沒腦子的增加return直接導致的是:
(1).查找的Key在第一層第一個鍵值對的值中,且遞歸調用時,Key也在目標字典的第一個位置,能夠返回正確值;
如:Key='H',Key=‘ACID’,Key=‘kkk’都能返回正確值,如果Key=‘B’,Key=‘CInf’只會返回None
(2).換句話說:for循環里只會使用第一對(k,v)
分析:
(1).如果在代碼最后加一個else:return None呢?事實證明這樣仍然會中斷for循環,沒有任何改進的作用。
(2).必須增加一個處理方法,讓程序能夠在for循環中循環下去,不能只局限在第一對(k,v)中。
主要就是用莫條件限制return ret是否執行,如果此return不執行,則for能繼續循環下去
如果ret是None就繼續循環,如果ret不是None就證明找到目標,應該return ret,精簡之后語句:if ret is not None: return ret
到此就算結束了,已經全部修改完成,雖然和參考文章上的代碼有些在default的區別,但功能已經完善了。經過如下測試:
正向測試:
print (recursionSearch(targetDict,'H')) #{'Ver': ['aaaa', 'bbbb'], 'ACID': {'kkk': 'aaaaa'}, 'CInf': [100, 2, 1000]}
print (recursionSearch(targetDict,'ACID')) #{'kkk': 'aaaaa'}
print (recursionSearch(targetDict,'kkk‘’)) #aaaaa
print (recursionSearch(targetDict,'Ver')) #['aaaa', 'bbbb']
print (recursionSearch(targetDict,'B')) #{'Login': {'Type': 'LP', 'Name': '41SS676', 'Pwd': {'aaa': '123456'}, 'ForToken': 1}}
print (recursionSearch(targetDict,'Login')) #{'Type': 'LP', 'Name': '41SS676', 'Pwd': {'aaa': '123456'}, 'ForToken': 1}
print (recursionSearch(targetDict,'Pwd')) #{'aaa': '123456'}
print (recursionSearch(targetDict,'aaa')) #123456
以上均能返回正確目標鍵的值 --測試通過
逆向測試:
print (recursionSearch(targetDict,'aaaaaa')) #None沒有鍵屬性是‘aaaaaa’的,只有一個鍵的值是‘aaaaaa’,測試函數是否是按鍵名查找
print (recursionSearch(targetDict,'B111')) #None
以上均能爭取返回None --測試通過
至此,從最初級錯誤程序,一步一步走到正確程序。
我的同事和我討論了一天,最終弄清楚了正確程序的原理,也一步一步分析清楚錯誤程序錯在哪里,應該如何改進。