解决python字典update浅更新的问题

解决python字典update浅更新的问题

一、问题复现

python字典中的update使用的是浅拷贝,也就是对于嵌套的内容更新采用的是直接覆盖。演示如下:

if __name__ == '__main__':
    d1 = {1: 1231, 2: {123: 2312, 'name': 'zhangs', 'hhh': 'gggg'}, 3: 3242}
    d2 = {1: 000, 2: {3: {1, 2, 3, 43}}}
    d1.update(d2)
    print(d1)
    print(d2)

代码执行结果如下:

{1: 0, 2: {3: {1, 2, 3, 43}}, 3: 3242}
{1: 0, 2: {3: {1, 2, 3, 43}}}

很明显对于d1[2]的内容是直接被d2[2]的内容覆盖了,这明显和我们的常理不符合

二、解决方法

使用递归的方法对字典进行更新,这样就可以解决覆盖问题,相应的函数如下:

def update(data: dict, new_data: dict):
    """ 对字典进行深度update """
    for key, item in new_data.items():
        if not data.get(key):
            # 如果需要更新字典的key中在原来字典中没有,就添加上
            data[key] = new_data[key]
        elif isinstance(item, dict):
            # 如果需要更新的是字典,就递归更新
            update(data[key], new_data[key])
        else:
            # 如果需要更新的是普通值,就直接修改
            data[key] = item

该函数实现了递归更新的功能,效果如下:

if __name__ == '__main__':
    d1 = {1: 1231, 2: {123: 2312, 'name': 'zhangs', 'hhh': 'gggg'}, 3: 3242}
    d2 = {1: 000, 2: {3: {1, 2, 3, 43}}}
    # d1.update(d2)
    update(d1, d2)
    print(d1)
    print(d2)

运行结果:

{1: 0, 2: {123: 2312, 'name': 'zhangs', 'hhh': 'gggg', 3: {1, 2, 3, 43}}, 3: 3242}
{1: 0, 2: {3: {1, 2, 3, 43}}}

三、优化之处

上面的代码已经实现了深度更新的功能,但还是存在一部分问题。
问题描述:上述代码对于d1原本没有的key是采用直接赋值,这样对于基本数据类型还好说,但对于引用类型的数据就存在问题。因为是把d2的引用赋值给了d1,如果d2对应的内容修改了,那么d1的内容也会发生变化。
问题复现

if __name__ == '__main__':
    d1 = {1: 1231, 2: {123: 2312, 'name': 'zhangs', 'hhh': 'gggg'}, 3: 3242}
    d2 = {1: 000, 2: {3: {1, 2, 3, 43}}}
    update(d1, d2)
    print('修改d2前d1的值', d1)
    d2[2][3].remove(43)
    print('修改d2后d1的值', d1)

运行结果如下:

修改d2前d1的值 {1: 0, 2: {123: 2312, 'name': 'zhangs', 'hhh': 'gggg', 3: {1, 2, 3, 43}}, 3: 3242}
修改d2后d1的值 {1: 0, 2: {123: 2312, 'name': 'zhangs', 'hhh': 'gggg', 3: {1, 2, 3}}, 3: 3242}

这两次打印的都是d1的值,在代码中也没有修改d1的值,但这两次的结果确不一样,这就是对应引用数据类型直接赋值会产生的问题

优化代码
对于该问题,可以使用应用类型的copy方法解决,代码如下:

def update(data: dict, new_data: dict):
    """ 对字典进行深度update """
    for key, item in new_data.items():
        if not data.get(key):
            # 如果需要更新字典的key中在原来字典中没有,就添加上
            # data[key] = new_data[key]
            data[key] = new_data[key].copy() if hasattr(new_data[key], 'copy') else new_data[key]
        elif isinstance(item, dict):
            # 如果需要更新的是字典,就递归更新
            update(data[key], new_data[key])
        else:
            # 如果需要更新的是普通值,就直接修改
            data[key] = item

优化后该问题就不存在了,当然copy也是浅拷贝,也会存在相应的问题,但优化的思路一致,这里就不复述了。

四、相关代码

def update(data: dict, new_data: dict):
    """ 对字典进行深度update """
    for key, item in new_data.items():
        if not data.get(key):
            # 如果需要更新字典的key中在原来字典中没有,就添加上
            # data[key] = new_data[key]
            data[key] = new_data[key].copy() if hasattr(new_data[key], 'copy') else new_data[key]
        elif isinstance(item, dict):
            # 如果需要更新的是字典,就递归更新
            update(data[key], new_data[key])
        else:
            # 如果需要更新的是普通值,就直接修改
            data[key] = item

如果不需要解决引用类型的问题,直接取消掉注释的代码即可,具体修改看上述文章所写内容。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值