You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python中二维列表不同初始化方式行为差异的原因是什么?

为什么用[[0]*3]*3初始化二维列表会出现"牵一发动全身"的问题?

这问题太常见啦!很多刚接触Python列表的朋友都会踩这个坑,我来给你掰扯清楚背后的逻辑~

先再明确下你遇到的两种现象:

  • 用嵌套列表推导式 square = [[0 for i in range(3)] for j in range(3)] 初始化后,修改 square[0][0] = 1,只有第一个子列表的第一个元素变成1,这是符合预期的正常行为。
  • 但用 square = [[0] * 3] * 3 初始化时,修改 square[0][0] = 1 后,三个子列表的第一个元素全变成了1,就像所有子列表是"绑定"在一起的一样。

核心原因:引用复用 vs 独立创建

问题出在[[0]*3] *3的执行逻辑上:

  1. 首先[0] * 3确实会创建一个新列表[0,0,0],这一步没问题。
  2. 但紧接着的*3,并不是把这个子列表复制三份,而是把这个子列表的内存引用重复添加了三次到外层列表中。也就是说,外层列表的三个元素,指向的是内存里同一个列表对象!

所以当你修改square[0][0]时,本质上是在修改那个唯一的子列表对象,而外层列表里的三个元素都是这个对象的引用,自然看起来三个子列表都变了。

而嵌套列表推导式[[0 for i in range(3)] for j in range(3)]就不一样了:外层的for j in range(3)每循环一次,都会执行一次内层的[0 for i in range(3)],每次都会创建一个全新的子列表。三个子列表是完全独立的内存对象,修改其中一个自然不会影响另外两个。

直观验证:用id()看内存地址

你可以用Python的id()函数来直观验证这个结论,它会返回对象的内存地址:

# 嵌套列表推导式创建的二维列表
square1 = [[0 for i in range(3)] for j in range(3)]
print(id(square1[0]), id(square1[1]), id(square1[2]))
# 输出三个不同的内存地址,说明是三个独立对象

# 用*3复用引用的二维列表
square2 = [[0]*3]*3
print(id(square2[0]), id(square2[1]), id(square2[2]))
# 输出三个完全相同的内存地址,说明指向同一个对象

正确的二维列表初始化方式

如果要创建包含独立子列表的二维列表,除了嵌套列表推导式,还可以用循环逐个添加:

square = []
for _ in range(3):
    # 每次循环都创建一个新的子列表
    square.append([0] * 3)

这样初始化后的二维列表,修改任意子列表的元素都不会影响其他子列表。

内容的提问来源于stack exchange,提问作者Antoine Guenet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 08:50:27