1. 程式人生 > >Python遞歸中 return 代碼陷阱

Python遞歸中 return 代碼陷阱

記錄 之前 查找 cheng 必須 sed display ive 技術

最近寫接口測試程序中,需要查詢多層嵌套字典中某個Key的值,查到則返回,查不到返回None,查詢的Key可以是任意層次的Key,如 Value本身也是多層字典,或者Key已經是葉子結點。

思想:利用Python的遞歸思想,逐層深入遍歷,最後返回結果值

最終的成品代碼參考了一下博客內容:

http://www.cnblogs.com/hahaweixiaohenqingcheng/archive/2016/11/14/6062961.html#undefined 嘗試多次後發現參考代碼已經無法再深入優化,只能照搬: 技術分享
 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
View Code

測試數據,拼接在上面的代碼裏即可

技術分享
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輸出:

41SS676
None
[Finished in 0.2s]

但為毛函數結果是None呢???

分析:

(1).代碼確實能夠達到葉子結點查找到目標鍵名的功能,但。。。還是嘗試著換換思路吧

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遞歸調用的結果

結果:這種代碼只能按照第一個元素這條線深入遞歸下去,無論最終找到或者找不到目標值,都會結束遞歸。

這種沒腦子的增加return直接導致的是:

(1).查找的Key在第一層第一個鍵值對的值中,且遞歸調用時,Key也在目標字典的第一個位置,能夠返回正確值;

如:Key=‘H‘,Key=‘ACID’,Key=‘kkk’都能返回正確值,如果Key=‘B’,Key=‘CInf’只會返回None

(2).換句話說:for循環裏只會使用第一對(k,v)

分析:

(1).必須增加一個處理方法,讓程序能夠在for循環中循環下去,不能只局限在第一對(k,v)中。

  主要就是用莫條件限制return ret是否執行,如果此return不執行,則for能繼續循環下去

  如果ret是None就繼續循環,如果ret不是None就證明找到目標,應該return ret,精簡之後語句:if ret is not None: return ret

正向測試:

  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

至此,從最初級錯誤程序,一步一步走到正確程序。

感謝我的同事 相開征 和我不厭其煩的討論了一天,最終弄清楚了正確程序的原理,也一步一步分析清楚錯誤程序錯在哪裏,應該如何改進。

Python遞歸中 return 代碼陷阱