Python中两种初始化二维数组方式的比较
在Python中,dp = [[0] * n for _ in range(m)]
和 dp = [[0 for _ in range(n)] for _ in range(m)]
确实能达到相同的效果——创建一个m行n列的二维数组,并将所有元素初始化为0。但它们内部实现机制有所不同,在某些特殊情况下会表现出差异。
两种写法的等价性
基本情况下(元素为不可变对象如0)
当数组元素是不可变对象(如整数0)时,两种写法完全等价:
m, n = 3, 4
# 写法1
dp1 = [[0] * n for _ in range(m)]
# 写法2
dp2 = [[0 for _ in range(n)] for _ in range(m)]
print(dp1 == dp2) # 输出: True
内部机制差异
写法1:[[0] * n for _ in range(m)]
- 先创建包含n个0的列表
[0, 0, ..., 0]
- 然后为每一行创建这个列表的独立副本
写法2:[[0 for _ in range(n)] for _ in range(m)]
- 为每一行的每个元素单独执行
0
的创建 - 每个元素都是独立的新对象
关键区别场景(当元素是可变对象时)
当初始化可变对象(如列表、字典等)时,两种写法会产生重大区别:
m, n = 3, 4
# 写法1 - 有潜在问题
dp_problem = [[[]] * n for _ in range(m)]
dp_problem[0][0].append(1)
print(dp_problem)
# 输出: [[[1], [1], [1], [1]],
# [[1], [1], [1], [1]],
# [[1], [1], [1], [1]]]
# 所有行的第一个元素都被修改了!
# 写法2 - 正确的做法
dp_correct = [[[] for _ in range(n)] for _ in range(m)]
dp_correct[0][0].append(1)
print(dp_correct)
# 输出: [[[1], [], [], []],
# [[], [], [], []],
# [[], [], [], []]]
# 只有指定的元素被修改
性能比较
对于简单不可变对象(如0):
- 写法1稍快,因为
[0]*n
是C语言层面的优化操作 - 写法2更通用,但稍慢
测试代码:
import timeit
m, n = 1000, 1000
def method1():
return [[0] * n for _ in range(m)]
def method2():
return [[0 for _ in range(n)] for _ in range(m)]
print(timeit.timeit(method1, number=10)) # 约0.5秒
print(timeit.timeit(method2, number=10)) # 约0.7秒
最佳实践建议
-
对于不可变对象(如0、None等):
- 两种写法都可以,
[[0]*n for _ in range(m)]
性能稍好
- 两种写法都可以,
-
对于可变对象(如[]、{}等):
- 必须使用
[[... for _ in range(n)] for _ in range(m)]
写法 - 避免使用
[...]*n
,否则会导致行间共享引用
- 必须使用
-
代码可读性考虑:
- 如果团队不熟悉Python的这个特性,写法2更明确
- 如果追求性能且确认元素不可变,写法1更简洁
结论
当元素是不可变对象(如0)时,两种初始化方式在功能上是等价的,但[[0]*n for _ in range(m)]
性能稍好;当元素是可变对象时,必须使用[[0 for _ in range(n)] for _ in range(m)]
写法,否则会导致意外的引用共享问题。