当前位置: 首页 > news >正文

python中二维数组初始化陷阱

python变量是一个储存对象地址的变量(可以看作为一个动态标签,可以表示任意数据类型)

matrix1 = [[0] * 3 for _ in range(3)] matrix2 = [[0] * 3] * 3

matrix2 = [[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

现在外层再次使用* 3,Python 同样复制了 3 次内部列表的地址。

matrix ──> [ 索引 0 , 索引 1 , 索引 2 ] │ │ │ └─────────┼─────────┘ ▼ [0, 0, 0] (可变对象)
为什么这时候崩盘了?

因为内部列表是可变对象,它支持原地修改(比如通过append()[0]=1)。

  • 当你执行matrix[0][0] = 1时,你没有去掐断matrix对子列表的连线。
  • 你是顺着索引 0的连线,直接走进了那个子列表的内部,把里面的第一个元素改成了1

因为索引 1索引 2连接的也是这同一个子列表的大门,当你从大门走进去看时,里面已经被改掉了。

正确写法

如果你想创建独立的行,必须让 Python每次都运行一次外壳,真正去新建一个列表。最推荐、最 Pythonic 的写法是使用列表推导式(List Comprehension)

Python

matrix = [[0] * 3 for _ in range(3)]

为什么这样就行了?因为for _ in range(3)是一个循环。在这个循环里,内层的[0] * 3独立执行了 3 次。每次执行都会在内存中开辟一块全新的、干净的空间来存放新列表。

此时的内存结构就变成了:

Plaintext

matrix ──> [ 索引 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()结果完全一样,就证明它们在内存中是同一个东西。

💻 代码验证:

Python

matrix = [[0, 0, 0]] * 3 # 打印三行的 id(内存地址) print(id(matrix[0])) # 输出类似: 140228345739520 print(id(matrix[1])) # 输出类似: 140228345739520 print(id(matrix[2])) # 输出类似: 140228345739520 # 三个地址一模一样,说明它们在内存里其实是同一个箱子!

⚠️ 延伸:==is的巨大区别

这也是面试中几乎必考的考点。请一定要分清:

  • ==(重在内容):检查两个对象的值(内容)是否相等。
  • is(重在身份):检查两个对象是不是同一个内存地址。

Python

list_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 (因为它们是分别创建的,住在不同的内存箱子里)
http://www.jsqmd.com/news/862794/

相关文章:

  • (QBuffer配合 QDataStream)二进制序列化
  • 影刀RPA 从0到1:自动化系统架构收敛与工程化演进总结
  • 面向诊断场景的云产品知识库设计方案
  • 今日实测有效的淘宝闪购外卖/京东外卖/美团外卖红包天天领取口令怎么领今天可用的外卖红包神券?
  • GPT5.5位置编码从绝对到相对的演进这个变化影响了上下文质量
  • 如何找到最适合你的私有化IM?
  • DDD 中的代码组织:按技术层分 vs 按领域模块分,哪种才是正解?
  • Light: Science Applications | 从平坦能带到量子行走:非阿贝尔Thouless泵浦的新篇章
  • 搜索引擎精准找免费行业报告?掌握这些关键词技巧就够了
  • 随钻连斜传感器操作手册:定向探管安装调试、故障排查与保养要点
  • 2026最新诚信优选 安庆市迎江区黄金回收白银回收铂金回收彩金回收门店TOP5排行榜+联系方式推荐_转自TXT - 盛世金银回收
  • 如何让Mac永不休眠:自动鼠标移动器的终极指南
  • 【零基础部署】Docker 部署 n8n 自动化工作流保姆级教程
  • 深入解析Hash碰撞:原理、成因与主流解决方案
  • 今天实测有效!2026淘宝京东天猫618红包领取口令最新推荐怎么天天领618淘宝京东天猫红包?
  • 2026最新诚信优选 安顺市平坝区黄金回收白银回收铂金回收彩金回收门店TOP5排行榜+联系方式推荐_转自TXT - 盛世金银回收
  • 2026最新诚信优选 安顺市西秀区黄金回收白银回收铂金回收彩金回收门店TOP5排行榜+联系方式推荐_转自TXT - 盛世金银回收
  • 2026年设计行业必备!兴弘实战设计培训班速成班究竟有多牛?
  • HYPE分布式水文模型建模方法与案例分析实践技术应用:精准完成子流域划分;系统解锁土地利用、土壤数据提取技巧
  • 轻量化无广告!开箱即用 M3U8 在线播放器,调试预览一步到位
  • fpc参数说明
  • 2026最新诚信优选 安阳市龙安区黄金回收白银回收铂金回收彩金回收门店TOP5排行榜+联系方式推荐_转自TXT - 盛世金银回收
  • 开发一个小程序需要多少钱?2026 行业收费标准及石家庄优质开发服务商推荐
  • # 如何从控制台获取
  • 终极Mac微信插件:消息防撤回与多开登录完整指南
  • Google三星AI眼镜来了,开发者该关注什么
  • 向日葵远程控制16.5发布,“免密远控”功能登场便捷又安全
  • 2026年企业AI落地新趋势!RAG知识库实战指南:环境搭建到生产部署全解析
  • 英雄联盟Akari助手:提升游戏效率的终极开源工具
  • WTEW的操作记录