默认的dict.update()在处理dict中包含list/dict对象时候会直接进行替换,不能取并集,在某些场景下不满足需求。针对这一问题,网上有很多讨论的内容,stackoverflow这个帖子比较经典,可以参考
https://stackoverflow.com/questions/3232943/update-value-of-a-nested-dictionary-of-varying-depth#
综合上面的内容,整理一个比较简单能处理大部分场景的函数实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import copy import collections def deep_update(orig_dict, new_dict): for key, val in dict(new_dict).items(): if key in orig_dict.keys(): if isinstance(val, collections.Mapping): tmp = deep_update(orig_dict.get(key, { }), val) orig_dict[key] = tmp elif isinstance(val, list): orig_dict[key] = (orig_dict[key] + val) else: orig_dict[key] = new_dict[key] else: orig_dict[key] = val return orig_dict d1={"id": 1, "name":"john", "score": {"chinese": 90}, "hobby": ["football",""]} d2={"age":30,"address":"23940 Allen Underpass Hobbsside, NJ 59822","score":{"english":80}, "hobby": ["basketball"]} print(deep_update(d1,d2)) # 运行结果 {'name': 'john', 'age': 30, 'score': {'chinese': 90, 'english': 80}, 'address': '23940 Allen Underpass Hobbsside, NJ 59822', 'hobby': ['football', '', 'basketball'], 'id': 1} |
python3里面有一个库pydantic,可以实现类似的操作,但是对dict里面的list会直接覆盖,而且不支持python2,所以有时还是须根据需要适当的改轮子
1 2 3 4 5 6 7 |
from pydantic.utils import deep_update as dp print(dp(d1, d2)) # 返回结果 {'id': 1, 'name': 'john', 'score': {'chinese': 90, 'english': 80}, 'hobby': ['basketball'], 'age': 30, 'address': '23940 Allen Underpass Hobbsside, NJ 59822'} |
另外补充一个递归获取dict对象中key的值,如果有循环嵌套的dict或者list也可以递归处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
def deep_get(orig_dict, mkey): """ 获取dict里面第一个查到的key值 """ do_recusive = True mvalue = orig_dict.get(mkey, None) if mvalue is not None: return mvalue else: for key, val in dict(orig_dict).items(): print(f"check key {key}:{val}") if isinstance(val, dict): return deep_get(val, mkey) # 如果获取到的是list elif isinstance(val, list): for item in val: print(f"check item in list: {item}") if isinstance(item, dict): return deep_get(item, mkey) |