传的是值还是引用? 构造字典数组的一个bug

在做数据处理的时候, 碰到一个奇怪的现象.

for tuple in list.most_common():
    dict['name'] = tuple[0]
    dict['value'] = tuple[1]
    d1.append(dict)   
print(d1)

[{‘name’: ‘大兴’, ‘value’: 32}, {‘name’: ‘大兴’, ‘value’: 32}, {‘name’: ‘大兴’, ‘value’: 32}, {‘name’: ‘大兴’, ‘value’: 32}, {‘name’: ‘大兴’, ‘value’: 32}, {‘name’: ‘大兴’, ‘value’: 32}, {‘name’: ‘大兴’, ‘value’: 32}]

发现d1这个列表里的每个元素都是一样的. 检查循环里的tuple 和 dict 一切正常.
循环里的d1 就不太正常…

for tuple in list.most_common():
    dict['name'] = tuple[0]
    dict['value'] = tuple[1]
    d1.append(dict)
    print(d1)

[{‘name’: ‘朝阳’, ‘value’: 102}]

[{‘name’: ‘海淀’, ‘value’: 65}, {‘name’: ‘海淀’, ‘value’: 65}]

[{‘name’: ‘昌平’, ‘value’: 53}, {‘name’: ‘昌平’, ‘value’: 53}, {‘name’: ‘昌平’, ‘value’: 53}]

[{‘name’: ‘丰台’, ‘value’: 53}, {‘name’: ‘丰台’, ‘value’: 53}, {‘name’: ‘丰台’, ‘value’: 53}, {‘name’: ‘丰台’, ‘value’: 53}]

[{‘name’: ‘西城’, ‘value’: 48}, {‘name’: ‘西城’, ‘value’: 48}, {‘name’: ‘西城’, ‘value’: 48}, {‘name’: ‘西城’, ‘value’: 48}, {‘name’: ‘西城’, ‘value’: 48}]

部分数据是这样的. 发生了append的操作, 但列表里所有元素的值都一样. 为什么会这样?

几个小时后, 想到了一种可能性. 在append操作的时候, 传入的是一个引用, 导致后期dict变化, 前面元素的值也跟着变化. 解决方法是传入一个dict的copy, 传一个值, 而不是引用.

修改后的代码:

for tuple in list.most_common():
    dict['name'] = tuple[0]
    dict['value'] = tuple[1]
    d = copy.copy(dict)
    d1.append(d)
print(d1)

[{‘name’: ‘朝阳’, ‘value’: 102}, {‘name’: ‘海淀’, ‘value’: 65}, {‘name’: ‘丰台’, ‘value’: 53}, {‘name’: ‘昌平’, ‘value’: 53}, {‘name’: ‘西城’, ‘value’: 48}, {‘name’: ‘通州’, ‘value’: 36}, {‘name’: ‘大兴’, ‘value’: 32}, {‘name’: ‘东城’, ‘value’: 22}, {‘name’: ‘顺义’, ‘value’: 18}, {‘name’: ‘石景山’, ‘value’: 16}, {‘name’: ‘NULL’, ‘value’: 11}, {‘name’: ‘房山’, ‘value’: 7}, {‘name’: ‘门头沟’, ‘value’: 6}, {‘name’: ‘燕郊’, ‘value’: 5}, {‘name’: ‘亦庄开发区’, ‘value’: 3}]

ok, 确实是假想的那样, 传引用产生的问题, 加上一个拷贝语句就可以了. 可是之前也做过不少的利用append来构造字典数组的案例, 从没碰到过问题, 也没有添加过拷贝. 仔细观察之下, 发现我的 dict= {} 声明是放在for循环外的. 那这应该就是问题所在了.

再次修改后的代码:

for tuple in list.most_common():
    # 把dict的定义放入 for 循环内
    dict = {}
    dict['name'] = tuple[0]
    dict['value'] = tuple[1]
    # d = copy.copy(dict)
    d1.append(dict)
print(d1)

总结一下, 由于疏忽把dict= {}放在for循环外, 在append的时候, 一直添加的是同一个dict的引用, dict 内的值变化, 引起了列表内所有元素的变化.
在最后的修改里, 每一次循环, 都会新建一个空字典, 每次循环 dict 都是一个独立的值. 正确的做法也应该是这样的.

PS: 尝试了一下, Js的arr.push(obj) 也有同样的问题, 当然有良好编码习惯的人是不会碰到这种问题的.

    原文作者:开罐之王777
    原文地址: https://segmentfault.com/a/1190000014910362
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞