发现一个 Python bug,最初以为是引用问题,后来逐步 print 看还真是 bug

查看 112|回复 9
作者:llsquaer   
列表字典中 随机一个字典增加 key ,再放入新列表中。出现预期不符。
直接上代码
有 bug 的情况
aaa = [
    {'id': 35,'src':'xxx'},
    {'id': 36,'src':'xxx'},
    {'id': 37,'src':'xxx'},
    {'id': 38,'src':'xxx'},
]
combinations = []
for i in range(5):
    cname = f'张三-{i}'
    ccc = random.choice(aaa)
    ccc.update({'cname': cname})
    print(ccc)                  # 这里的结果符合预期
    combinations.append(ccc)
print(combinations)             # 但是这里就错了
返回结果
{'id': 37, 'src': 'xxx', 'cname': '张三-0'}
{'id': 38, 'src': 'xxx', 'cname': '张三-1'}
{'id': 35, 'src': 'xxx', 'cname': '张三-2'}
{'id': 38, 'src': 'xxx', 'cname': '张三-3'}
{'id': 36, 'src': 'xxx', 'cname': '张三-4'}
# 以上 print 结果是对的
[{'id': 37, 'src': 'xxx', 'cname': '张三-0'}, {'id': 38, 'src': 'xxx', 'cname': '张三-3'}, {'id': 35, 'src': 'xxx', 'cname': '张三-2'}, {'id': 38, 'src': 'xxx', 'cname': '张三-3'}, {'id': 36, 'src': 'xxx', 'cname': '张三-4'}]
# 但是这里打印新生成的 combinations 列表就出现两个 `张三-3`
改代码
后来想起来是引用对象问题,需要浅复制下.即只需要将 ccc = random.choice(aaa)改为ccc = random.choice(aaa).copy() 就符合预期了.
bug 的疑问
bug 问题在于示例里,单个 print 结果和添加到列表里的结果不一致.
python 版本 3.10.8
darcyC   
你 print 的是当时那一刹那的值哦,你最后所谓的列表里的内容是最后 print 的哦,那也是那一刹那的值哦。
thinkershare   
这个根本就不是 bug ,就是可变对象的问题,你的 list 里面在最后的时候,有两个位置的元素引用了同一个对象。仅此而已。
llsquaer
OP
  
@darcyC 如果 print 的值是正确的,那么添加到 combinations 里的值应该也是按照 print 顺序进行添加的啊,再之后并没有做修改字典的动作了。
ktyang   
这是来钓鱼的嘛?
thinkershare   
@ktyang 感觉的确有钓鱼的嫌疑。
llsquaer
OP
  
@thinkershare 应该是对象引用问题,但是为啥 print 的值是对的?没错乱
thinkershare   
@llsquaer
因为状态随着时间的流逝被改变了(代码在线程上执行,已执行代码随时都可以改变内存中对象的状态).
一个对象在不同时刻,完全可以显示不同状态,print 是需要将对象转换为字符串(字符串序列化).
这个转化的时刻,会冻结那个时刻对象的字符串表示,而随着代码继续执行,这个对象被改变了。
print 打印时候首先要获得这个对象的字符串序列化表示,然后调用系统提供的接口将字符串使用指定字体在屏幕上渲染出来,因此一切都要看某一个时刻的状态。你不能对比不同时刻对一个对象状态(除非这个对象是不可变对象).
这个过程看似简单,实际还是涉及到很多乱七八糟的概念。
DOLLOR   
@llsquaer
“再之后并没有做修改字典的动作了”——这话错了。
后面循环的时候,random.choice 仍会抽取到之前同一个 ccc ,然后 update 掉了。
你要明白一点,把 aaa 里的元素直接 append 到 combinations 里,combinations 的元素跟 aaa 的元素都是相同的引用。
任何对 aaa 元素的修改,都会影响到 combinations 里的元素。
类似的例子
list1 = [{'name': '张三'}]
list2 = []
# 抽取 list1 的元素,加入 list2
item = list1[0]
list2.append(item)
print(list1, list2) # 都是 [{'name': '张三'}]
item['name'] = '李四' # 修改了 list1 里的 item ,但 list2 里的也跟着变了
print(list1, list2) # 都是 [{'name': '李四'}]
fatigue   
学学用调试器吧,愁
您需要登录后才可以回帖 登录 | 立即注册

返回顶部