Python遞歸中 return 代碼陷阱


最近寫接口測試程序中,需要查詢多層嵌套字典中某個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
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輸出:

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

 

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

這種沒腦子的增加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   --測試通過

 

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

我的同事和我討論了一天,最終弄清楚了正確程序的原理,也一步一步分析清楚錯誤程序錯在哪里,應該如何改進。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM