python中二维数组初始化陷阱

发布时间:2026/5/22 6:38:23

python中二维数组初始化陷阱 python变量是一个储存对象地址的变量可以看作为一个动态标签可以表示任意数据类型matrix1 [[0] * 3 for _ in range(3)] matrix2 [[0] * 3] * 3matrix2 [[0]*3]*3的运行步骤1. 第一步[0] * 3当你写[0] * 3时Python 确实把0的内存地址复制了 3 份。在底层这个列表其实长这样row ── [ 索引 0 , 索引 1 , 索引 2 ] │ │ │ └─────────┼─────────┘ ▼ [ 0 ] (不可变对象)你看 navigation 里的三个索引其实都指向同一个0。这和第二步的连线方式一模一样为什么说它是安全的因为0是不可变对象。你没有任何办法去“原地修改”这个0。如果你执行row[0] 5你并不是把0变成了5。你做的是在内存里找个新地方放5然后把索引 0的连线掐断重新连到5上。row ── [ 索引 0 , 索引 1 , 索引 2 ] │ │ │ ▼ └─────────┤ [ 5 ] ▼ [ 0 ]此时索引 1和索引 2依然雷打不动地指向0。所以结果是[5, 0, 0]完美符合预期。2. 第二步[[0, 0, 0]] * 3现在外层再次使用* 3Python 同样复制了 3 次内部列表的地址。matrix ── [ 索引 0 , 索引 1 , 索引 2 ] │ │ │ └─────────┼─────────┘ ▼ [0, 0, 0] (可变对象)为什么这时候崩盘了因为内部列表是可变对象它支持原地修改比如通过append()或[0]1。当你执行matrix[0][0] 1时你没有去掐断matrix对子列表的连线。你是顺着索引 0的连线直接走进了那个子列表的内部把里面的第一个元素改成了1。因为索引 1和索引 2连接的也是这同一个子列表的大门当你从大门走进去看时里面已经被改掉了。正确写法如果你想创建独立的行必须让 Python每次都运行一次外壳真正去新建一个列表。最推荐、最 Pythonic 的写法是使用列表推导式List ComprehensionPythonmatrix [[0] * 3 for _ in range(3)]为什么这样就行了因为for _ in range(3)是一个循环。在这个循环里内层的[0] * 3被独立执行了 3 次。每次执行都会在内存中开辟一块全新的、干净的空间来存放新列表。此时的内存结构就变成了Plaintextmatrix ── [ 索引 0 , 索引 1 , 索引 2 ] │ │ │ ▼ ▼ ▼ [0,0,0] [0,0,0] [0,0,0] 三个独立的列表互不干扰 核心总结用一句话把这个行为说透Python 的*操作符一视同仁永远只复制“引用地址”。复制不可变对象的地址是安全的因为没人能原地改它要改只能换新地址解绑。复制可变对象的地址是危险的因为任何人都可以顺着地址进去把里面的内容改掉从而连累所有指向它的人。特性不可变对象 (Immutable)可变对象 (Mutable)包含类型int,float,str,tuple,boollist,dict,set能否原地修改❌ 不能能修改时的底层操作放弃原对象创建全新对象开辟新内存直接修改原对象内部的值内存地址不变常见误区以为重新赋值就是修改了原对象容易像上一题一样多个变量共享同一个内存导致“牵一发而动全身”关于验证两个变量比如两个列表是否指向同一个内存地址方法一使用is运算符最推荐、最直观is是 Python 的身份运算符。它专门用来检查两个变量的内存地址是否完全相同。如果返回True说明它们指向同一个内存地址是同一个对象。如果返回False说明它们在内存中是相互独立的哪怕它们的内容长得一模一样。 代码验证Python# 情况 1使用 * 3 复制导致的“同地址”陷阱 matrix [[0, 0, 0]] * 3 # 检查第一行和第二行是不是同一个列表 print(matrix[0] is matrix[1]) # 输出: True 完蛋指向同一个地址 # 情况 2使用列表推导式创建的“独立”列表 correct_matrix [[0, 0, 0] for _ in range(3)] # 检查第一行和第二行是不是同一个列表 print(correct_matrix[0] is correct_matrix[1]) # 输出: False 安全互不干扰方法二使用id()函数看清本质id()是 Python 的内置函数它会返回一个对象的唯一内存地址在 CPython 中它通常表现为一个很长的整数代表该对象在内存中的实际物理地址。如果两个变量的id()结果完全一样就证明它们在内存中是同一个东西。 代码验证Pythonmatrix [[0, 0, 0]] * 3 # 打印三行的 id内存地址 print(id(matrix[0])) # 输出类似: 140228345739520 print(id(matrix[1])) # 输出类似: 140228345739520 print(id(matrix[2])) # 输出类似: 140228345739520 # 三个地址一模一样说明它们在内存里其实是同一个箱子⚠️ 延伸和is的巨大区别这也是面试中几乎必考的考点。请一定要分清(重在内容)检查两个对象的值内容是否相等。is(重在身份)检查两个对象是不是同一个内存地址。Pythonlist_a [1, 2, 3] list_b [1, 2, 3] print(list_a list_b) # 输出: True (因为里面的内容都是 1, 2, 3) print(list_a is list_b) # 输出: False (因为它们是分别创建的住在不同的内存箱子里)

相关新闻