Python 列表乘法:为什么改一行,三行都跟着变?
用列表乘法初始化一个三行两列的表格,看起来很省事。但如果三行指向同一个列表,改动一处单元格,其他行也会显示同样的变化。可以用很小的例子区分“重复引用”和“重新创建对象”。
1. 三行为什么一起变?
rows = [[0, 0]] * 3
rows[0][1] = 9
print(rows) # [[0, 9], [0, 9], [0, 9]]
print(rows[0] is rows[1]) # True
这个表达式先创建一个内层列表,再把对它的引用重复三次。外层有三个位置,内层却只有一个行对象。给 rows[0][1] 赋值是在修改这个共享对象,因此从另外两行也能看到改动。
这里用 is 检查的是对象身份。两行内容相同,并不能单独证明它们是否共享同一个对象;还要区分值相等和对象相同。
2. 让每一行都重新创建
rows = [[0, 0] for _ in range(3)]
rows[0][1] = 9
print(rows) # [[0, 9], [0, 0], [0, 0]]
print(rows[0] is rows[1]) # False
列表推导式每次迭代都执行一次内层列表表达式,生成独立的行对象。本例的单元格是整数,修改第一行的一个位置,不会改变另外两行。
“每行独立”也有边界。如果所有行里的单元格仍引用同一个外部字典,改字典内容时依然会相互影响。需要独立到哪一层,就应在那一层创建新对象,不能把最外层的推导式当成自动深拷贝。
3. 为什么一维的 [0] * 3 可以这样用?
values = [0] * 3
values[0] = 9
print(values) # [9, 0, 0]
这次赋值替换的是外层列表第一个位置保存的引用,没有修改整数 0。整数不可变,所以不能照搬“修改共享行列表”的过程来解释它。是否有风险,关键是后续修改发生在哪个对象上。
实际排查二维表格初始化时,可以先检查两行是否为同一对象,再分别验证“修改行里的值”和“替换整行”。前者可能影响共享行;后者只是替换外层的一个位置,不会自动替换其他行。
实际验证与官方参考
上述三段示例已在本机分别执行,并补充验证了重复次数为零或负数、重复一次仍共享内层对象,以及外部可变单元格等边界。示例只操作内存中的小列表,没有读写业务数据。
Python 官方文档 Built-in Types 的 Common Sequence Operations 小节说明了序列重复的语义,并给出嵌套列表共享引用的例子。可按以下官方路径核对:
docs.python.org/3/builtins/stdtypes.html#common-sequence-operations
AI 辅助说明:本文由 AI 协助组织文字与编写示例,代码已实际运行,相关语义已结合官方文档与独立断言核对。
- 点赞
- 收藏
- 关注作者
评论(0)