Python中值相等的两种2D数组初始化为何产生不同结果
核心差异根源:底层引用的共享机制
- 两种初始化的实际内存逻辑差异
- 列表推导式实现:
dp = [[float('inf') for _ in range(k+1)]for _ in range(n+1)]每一轮外层循环都会生成一个独立的、内存地址互不相同的子列表,每一行的存储完全隔离,修改任意一行的元素不会影响其他行。 - 列表乘法实现:
dp1 = [[float('inf')]*(k+1)]*(n+1)运算本质是将*运算符前生成的单个子列表的内存引用重复复制n+1次,整个二维列表的所有行,全部指向内存中同一个子列表对象,不存在逐行独立的存储结构。
- 列表推导式实现:
- 为什么assert校验会通过
Python的列表相等判断会逐元素逐一比对值。由于乘法生成的所有行本就是同一个对象,所有位置的初始值均为float('inf'),与列表推导式生成的数组初始值完全一致,因此dp == dp1的判断会返回真值,完全检测不出底层的引用差异,这也是问题极具迷惑性的核心原因。 - 为什么DP输出会出现本质偏差
动态规划求解过程需要按节点、按剩余通行额度逐次更新二维数组的状态值。使用乘法初始化的数组时,任意一行任意位置的元素修改,都会同步影响所有行的同位置值。
可以通过极简代码验证这个效果:
问题中出现的全0输出,本质就是乘法初始化的结构让所有行的状态被反复同步覆盖——原本按节点独立存储的最短路数值完全串值,状态转移逻辑完全失效,最终不管走到哪个节点,读取到的都是被同步覆盖的0值,自然输出全0;而列表推导式初始化的结构因为行与行完全隔离,状态可以独立转移计算,才能得到正确的n = 3 k = 2 bad_dp = [[float('inf')]*(k+1)]*(n+1) # 校验所有行的内存地址是否一致 print(id(bad_dp[0]) == id(bad_dp[1]) == id(bad_dp[2])) # 输出True,证明三行是同一个对象 bad_dp[0][0] = 100 print(bad_dp[1][0]) # 输出100,而非初始的inf,证明修改是全局同步的0 0 0 2 2结果。 - 常见认知误区澄清
一维列表的乘法如[0]*5可以正常使用,因为其存储的是整数这类不可变对象;但只要嵌套结构中存在列表这类可变对象,直接在外层使用列表乘法,就必然会触发引用共享的问题。
内容的提问来源于stack exchange,提问作者Ruchit Vithani
相关产品推荐
相关产品推荐

