轉載,出處 :https://www.cnblogs.com/Detector/p/8085460.html
背景
在做接口自動化的過程中,接口返回的數據是 列表字典循環嵌套 格式的,所以怎樣通過一個key值,獲取到被包裹了多層的目標數據成為了擺在我面前的一個問題。
一開始沒想自己寫,但是搜索后發現雖然很多人遇到類似的問題,但是相應的解決方案都不能達到我想要的結果,所以自己嘗試寫了一個。
思路
最初的做法是寫一個函數,每次對傳入的數據進行類型判斷,然后根據數據類型做對應的處理,后來發現如果這樣,實際有多少層數據就要做多少次判斷。
那么有沒有一勞永逸的方法呢?答案當然是有!
調試過程中發現,函數的調用特別符合遞歸的規律,但是和一般遞歸的略有不同,需要兩個函數相互調用遞歸。實測可以完美解決這個問題!
具體思路如下:
新建兩個函數A和B,函數 A處理字典數據,被調用后,判斷傳遞的參數,如果參數為字典,則調用自身;
如果是列表或者元組,則調用列表處理函數B;
函數 B處理列表,被調用后,判斷傳遞的參數,如果參數為列表或者元組,則調用自身;
如果是字典,則調用字典處理函數A;
參考代碼
注釋已經寫得比較清晰,就不多解釋了:
#! /usr/bin/python # coding:utf-8 """ @author:Bingo.he @file: get_target_value.py @time: 2017/12/22 """ def get_target_value(key, dic, tmp_list): """ :param key: 目標key值 :param dic: JSON數據 :param tmp_list: 用於存儲獲取的數據 :return: list """ if not isinstance(dic, dict) or not isinstance(tmp_list, list): # 對傳入數據進行格式校驗 return 'argv[1] not an dict or argv[-1] not an list ' if key in dic.keys(): tmp_list.append(dic[key]) # 傳入數據存在則存入tmp_list else: for value in dic.values(): # 傳入數據不符合則對其value值進行遍歷 if isinstance(value, dict): get_target_value(key, value, tmp_list) # 傳入數據的value值是字典,則直接調用自身 elif isinstance(value, (list, tuple)): _get_value(key, value, tmp_list) # 傳入數據的value值是列表或者元組,則調用_get_value return tmp_list def _get_value(key, val, tmp_list): for val_ in val: if isinstance(val_, dict): get_target_value(key, val_, tmp_list) # 傳入數據的value值是字典,則調用get_target_value elif isinstance(val_, (list, tuple)): _get_value(key, val_, tmp_list) # 傳入數據的value值是列表或者元組,則調用自身
效果圖
下圖對對這個方法做了測試,能從很復雜的多重嵌套數據中正常獲取到想要的值,測試數據:
test_dic = {'a': '1', 'b': '2', 'c': {'d': [{'e': [{'f': [{'v': [{'g': '6'}, [{'g': '7'}, [{'g': 8}]]]}, 'm']}]}, 'h', {'g': [10, 12]}]}}
調試過程中遇到的坑
初始時,博主將存儲獲取數據的臨時list放到了函數的參數里,這樣調用時候就可以少傳一個參數,但是后來發現,單次調用的時候不存在問題,但是多次調用的時候,會同時返回上一次調用的值,
這可能是python函數本身的一個bug
def get_target(a, b=[]): b.append(a) print(b) get_target(1)
get_target(2)
后來查了很多資料了解到,參數默認值,只會在函數聲明時初始化一次,之后不會再初始化
下面這段代碼定義和調用也是存在細微差別的
def foo(*args, **kargs): pass foo(*args, **kargs)