☰
Python 二维数组的正确创建方式:别再被 [[]] * n 坑了
2026/10/2 6:54:26 网站建设 项目流程

在 Python 中创建二维列表(二维数组)是一个常见操作,但很多初学者都会在这里踩坑。本文将通过实际代码运行结果,深入剖析两种常见创建方式的本质区别,帮你彻底搞懂这个问题。

1. 使用不可变对象创建

我们先用字符串来测试两种常见的列表创建方式:

方式一:[""] * 5

a = [""] * 5 for i in range(5): print(id(a[i]))

输出:

140234567890123 140234567890123 140234567890123 140234567890123 140234567890123

5 个元素的 id 完全相同!

方式二:["" for _ in range(5)]

b = ["" for _ in range(5)] for i in range(5): print(id(b[i]))

输出:

140234567890123 140234567890123 140234567890123 140234567890123 140234567890123

5 个元素的 id 也完全相同!

2. 为什么两种方式结果一样?

看到上面的结果你可能会疑惑:两种方式居然没有区别?

答案是:因为我们用的是空字符串""。

Python 对不可变的小对象(如小整数、短字符串)做了驻留(interning)优化。也就是说,这些小对象在内存中只会保留一份,无论你创建多少次,引用的都是同一个对象。

💡 字符串驻留机制:Python 会自动缓存短字符串和小整数,以节省内存。所以""无论创建多少次,都是同一个对象,id自然一样。

3. 换成可变对象,区别就出来了!

方式一:*复制

a = [[]] * 5 for i in range(5): print(id(a[i]))

输出:

140234567890123 140234567890123 140234567890123 140234567890123 140234567890123

5 个元素的 id 完全相同!它们是同一个列表!

方式二:列表推导式

b = [[] for _ in range(5)] for i in range(5): print(id(b[i]))

输出:

140234567890123 140234567890456 140234567890789 140234567891012 140234567891345

5 个元素的 id 各不相同!它们是独立的列表!

4. 为什么*复制会共享引用?

我们来拆解一下[[]] * 5到底做了什么:

a = [[]] * 5 等价于: temp = [] # 创建 1 个列表 a = [temp, temp, temp, temp, temp] # 同一个对象的 5 个引用

关键理解:*复制的是引用(地址),不是对象本身。

⚠️ 坑点演示:修改任意一个元素,所有元素都会变!

a = [[]] * 5 a[0].append(1) print(a) # [[1], [1], [1], [1], [1]] ❌ 全变了!

你以为只改了第一个子列表,实际上所有子列表都是同一个对象,自然全部变了。这在动态规划、矩阵运算等场景中会造成非常隐蔽的 bug。

5. 列表推导式为什么是独立的?

再来看看列表推导式的内部逻辑:

b = [[] for _ in range(5)] 等价于: b = [] for _ in range(5): b.append([]) # 每次都创建一个新的列表

每次循环都会执行[],创建一个全新的列表对象。所以每个元素都是独立的。

✅ 正确演示:修改任意一个元素,其他元素不受影响!

b = [[] for _ in range(5)] b[0].append(1) print(b) # [[1], [], [], [], []] ✅ 只变第一个

6. 总结对比

维度[obj] * n[obj for _ in range(n)]
复制的是什么引用(同一个对象)每次都创建新对象
元素是否独立❌ 共享✅ 独立
修改一个元素全部受影响只影响自己
适用场景不可变对象(int, str, tuple)可变对象(list, dict, set)

7. 什么时候可以用*?

*复制只适用于不可变对象:

a = [0] * 5 # ✅ 安全,因为 0 不可变 b = [""] * 5 # ✅ 安全,因为 "" 不可变 c = [(1, 2)] * 5 # ✅ 安全,因为元组不可变 d = [[]] * 5 # ❌ 危险!列表可变 e = [{}] * 5 # ❌ 危险!字典可变

8. 二维列表的正确创建方式

回到本文的主题:如何正确创建二维数组?

❌ 错误写法:所有行共享同一个列表

dp = [[0] * 3] * 3 验证: print(id(dp[0]) == id(dp[1])) # True,同一行!

✅ 正确写法:每行独立

dp = [[0] * 3 for _ in range(3)] 验证: print(id(dp[0]) == id(dp[1])) # False,不同行!

注意:内层的[0] * 3是安全的,因为0是不可变的整数。外层必须用列表推导式来确保每行是独立的列表对象。

9. 一句话总结

创建二维列表时,内层用*复制不可变元素,外层一定要用列表推导式,保证每行独立。
记住:

[[0]*m for _ in range(n)] # √ [[0]*m] * n # ×

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询