Python列表推导式与乘法初始化二维列表后修改结果差异原因
二维列表两种初始化方式的行为差异根因
核心本质
两种初始化方式输出的初始列表外观完全一致,但底层对独立对象的创建逻辑完全不同,列表乘法操作的核心特性是只复制对象引用,不会生成全新的独立对象,这是最终修改结果出现偏差的根本原因。
列表推导式初始化的执行逻辑
对应实现代码:
C1 = [[0 for x in range(r+1)] for x in range(n+1)]
- 外层循环每迭代一次,都会重新执行一次内层的列表推导逻辑,当场生成一个拥有独立内存地址的全新子列表
- 所有生成的子列表互相独立,不存在引用关联,因此单独修改任意子列表的元素,只会影响该子列表本身,不会波及其他子列表,完全符合赋值操作的预期效果。
列表乘法初始化的执行逻辑
对应实现代码:
C2 = [[0] * (r+1)] * (n+1)
分步拆解实际执行过程:
- 先执行内层的
[0] * (r+1):由于0是不可变的整型值,这一步会生成一个存储了7个0的独立子列表对象,记为对象A - 再执行外层的
[对象A] * (n+1):Python中列表乘法的固定规则是,将列表内已有元素的引用重复N次拼接为新列表,整个过程不会创建新的子列表,只是把指向对象A的引用复制6份,依次塞入外层列表
- 最终C2的所有元素,本质上都是指向同一个子列表对象A的引用,从初始化开始就不存在多个独立的子列表
- 当执行
C2[4][3] = 4的赋值操作时,实际修改的是那个唯一的公共子列表对象A的第3位元素,因此所有引用对象A的外层位置,都会同步显示修改后的内容,最终出现“修改单个元素,所有子列表同步变化”的现象。
直观验证方法
可以通过Python内置的id()函数查看对象的内存地址,直接验证底层差异:
- 对C1执行
id(C1[0]) == id(C1[4]),返回结果为False,说明两个位置是相互独立的不同对象 - 对C2执行
id(C2[0]) == id(C2[4]),返回结果为True,说明两个位置指向完全相同的同一个对象
内容的提问来源于stack exchange,提问作者Rahul M Dinesh
相关产品推荐
相关产品推荐

