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

Python里的“赋值”到底是什么意思?

免费编程软件「python+pycharm」
链接:https://pan.quark.cn/s/48a86be2fdc0

一个让我困惑了三年的问题

刚学Python那年,我写了这样一段代码:

a = [1, 2, 3] b = a b.append(4) print(a) # [1, 2, 3, 4] 什么?a也变了? print(b) # [1, 2, 3, 4]

我当时的心态是:我只改了b,为什么a也跟着变了?

更让我崩溃的是这个:

x = 5 y = x y = y + 1 print(x) # 5 —— 这次x没变 print(y) # 6

同样都是赋值,为什么一种情况变了,另一种没变?

我当时在网上搜了很久,得到的答案都是“可变对象和不可变对象的区别”。但这个解释太抽象了,我背下来也理解不了。

直到有一天,我明白了Python赋值的本质:变量不是盒子,是标签

这个比喻改变了一切。今天我把这个理解的过程分享给你。


重新理解“赋值”:不是装东西,是贴标签

大多数人学编程时,脑子里都有一个“变量是盒子”的模型:

a = 5 # 把5放进a这个盒子里 b = a # 把a盒子里的5复制一份,放进b盒子

这个模型在大部分情况下能工作,但遇到列表、字典时就翻车了。

用“盒子模型”解释不了为什么b = a之后改b会影响a。

正确的理解方式

  • Python的变量不是盒子,而是便利贴(标签)

  • 赋值不是在盒子里装东西,而是把标签贴到对象上

来看这个:

a = [1, 2, 3]

这句话的意思是:创建一个列表对象[1, 2, 3],然后把标签a贴在这个对象上。

b = a

这句话的意思是:把标签b也贴到a当前贴的那个对象上。

现在,ab两个标签贴在同一个对象上。

b.append(4)

append是找到b标签贴着的那个对象,然后往里面加个4。因为a也贴在这个对象上,所以用a去看的时候,自然也能看到那个4。

这就是为什么改b会影响a——不是b和a有什么关系,而是它们本来贴的就是同一个东西。


验证一下:用id()看看对象地址

Python里有个内置函数id(),可以返回对象的唯一标识(可以理解为内存地址)。

我们来验证一下:

a = [1, 2, 3] print(id(a)) # 比如输出 140234567890 b = a print(id(b)) # 输出同样的数字,说明指向同一个对象 b.append(4) print(id(a)) # 还是那个数字,a还是贴在同一个对象上

再看不可变对象:

x = 5 print(id(x)) # 比如 140234567123 y = x print(id(y)) # 同样的地址 y = y + 1 print(id(y)) # 新的地址!跟x不一样了 print(id(x)) # 没变,还是原来的地址

关键区别在于:

  • 列表是可变的,你可以修改对象本身(往里面加东西),对象还是那个对象,地址不变

  • 整数是不可变的,y = y + 1不是修改了原来的对象,而是创建了一个新对象(值为6),然后把标签y贴到这个新对象上

一句话总结:变量是标签,不是盒子。赋值就是贴标签。


这个理解能解释什么?

解释1:为什么函数参数传递这么“奇怪”

很多人觉得Python的函数参数传递很诡异,有时候函数内部能修改外部变量,有时候不能。

用“标签模型”一下就明白了:

def add_one(n): n = n + 1 print(id(n)) x = 5 print(id(x)) # 地址A add_one(x) # 地址B(新对象) print(x) # 还是5

流程是这样的:

  1. 调用add_one(x),参数n被贴上x当前贴的对象(值为5的那个对象)

  2. n = n + 1,计算5+1=6,创建新对象6,然后把标签n贴到新对象上

  3. 函数结束,标签n消失

  4. 标签x从头到尾还是贴在5上,没有动过

再看列表的情况:

def add_item(lst): lst.append(4) print(id(lst)) my_list = [1, 2, 3] print(id(my_list)) # 地址C add_item(my_list) # 地址C(同一个对象) print(my_list) # [1, 2, 3, 4] 变了

流程:

  1. 参数lst贴上my_list当前贴的对象(列表[1,2,3]

  2. lst.append(4),找到这个对象,往里面加东西

  3. 对象还是那个对象,地址没变

  4. 函数结束,lst标签消失,但my_list还是贴在同一个对象上,所以能看到修改

核心:函数参数传递的是对象的地址(标签的复制),不是对象的复制。

这就是为什么很多人说Python是“传对象引用”。


解释2:为什么两个列表的修改会互相影响

original = [1, 2, 3] copy = original # 这不是复制!是贴了两个标签 copy.append(4) print(original) # [1, 2, 3, 4]

如果你想要真正的复制,需要创建一个新对象:

original = [1, 2, 3] copy = original[:] # 切片创建新列表 # 或者 copy = original.copy() # 或者 copy = list(original) copy.append(4) print(original) # [1, 2, 3] —— 没变 print(copy) # [1, 2, 3, 4]

切片original[:]创建了一个新的列表对象,里面的元素是原列表元素的引用(对于不可变对象没问题,对于嵌套列表要小心——这就是浅拷贝的问题)。


解释3:为什么a = b = 1能用

你可能写过这样的代码:

a = b = 0

用标签模型很好理解:创建对象0,然后把标签a和标签b都贴上去。

等价于:

a = 0 b = a # 把b也贴到a贴的那个对象上

解释4:为什么a, b = b, a能交换值

Python的交换写法很优雅:

a = 10 b = 20 a, b = b, a print(a, b) # 20 10

背后发生了什么?

b, a先创建了一个元组(20, 10)(这是个临时对象),然后把这个元组里的值依次贴给ab

用标签模型理解:右边的表达式先计算出右边的对象(元组),然后把左边的标签一个个贴到对应的对象上。

所以交换不需要临时变量,因为本质是贴标签,不是倒腾盒子里的东西。


可变 vs 不可变:到底谁变了?

回到开头的困惑:为什么改b会影响a?

关键在于对象的类型

类型可变性例子修改对象本身
列表可变[1,2,3]append(),extend(),pop(), 索引赋值
字典可变{'a':1}dict['key']=value,update()
集合可变{1,2,3}add(),remove()
整数不可变5没有修改方法
浮点数不可变3.14没有修改方法
字符串不可变"hello"没有修改方法
元组不可变(1,2,3)没有修改方法
布尔不可变True没有修改方法

对于可变对象,你可以修改对象本身。贴在这个对象上的所有标签都会“看到”这个变化。

对于不可变对象,你无法修改对象本身。x = x + 1会创建新对象,然后把标签贴过去。其他标签不受影响。

有个办法能立刻判断:看操作有没有改变对象的内存地址(用id())。地址变了就是创建了新对象,地址没变就是修改了原对象。

# 可变对象——原地修改 lst = [1,2,3] print(id(lst)) lst.append(4) print(id(lst)) # 一样的地址 # 不可变对象——创建新对象 s = "hello" print(id(s)) s = s + " world" print(id(s)) # 不一样的地址

几个让人防不胜防的坑

坑1:默认参数的陷阱

def add_item(item, my_list=[]): my_list.append(item) return my_list print(add_item(1)) # [1] print(add_item(2)) # [1, 2] —— 意外! print(add_item(3)) # [1, 2, 3]

你期望每次调用都是一个新的空列表,但实际用的是同一个列表对象

原因:默认参数的值在函数定义时就被创建了。之后每次调用不传参数时,默认参数用的就是那个提前创建好的对象。

正确做法:

def add_item(item, my_list=None): if my_list is None: my_list = [] my_list.append(item) return my_list

坑2:浅拷贝 vs 深拷贝

original = [[1, 2], [3, 4]] shallow = original[:] # 浅拷贝 shallow[0].append(99) print(original[0]) # [1, 2, 99] —— 里面的列表还是同一个!

浅拷贝只复制了外层容器,里面的元素还是原来的标签。如果你有一个嵌套结构(列表套列表),浅拷贝只解决一层。

深拷贝才能完全独立:

import copy deep = copy.deepcopy(original) deep[0].append(88) print(original[0]) # 不受影响

经验法则:如果你的数据结构里只有不可变对象,浅拷贝够用。如果有嵌套的可变对象,考虑深拷贝。

坑3:把可变对象当字典的键

d = {} lst = [1, 2] d[lst] = "value" # TypeError: unhashable type: 'list'

字典的键必须是不可变的(可哈希的)。因为如果键是可变的,改了它之后,字典就找不到这个键了。

所以列表不能当字典的键,但元组可以:

d = {(1, 2): "value"} # 元组不可变,可以

一个面试题测试你的理解

猜猜下面这段代码的输出:

def test(a, b): a = a + 1 b.append(4) return x = 10 y = [1, 2, 3] test(x, y) print(x, y)

答案是:10 [1, 2, 3, 4]

  • x是整数(不可变),a = a + 1创建了新对象,不影响外面的x

  • y是列表(可变),b.append(4)修改了对象本身,外面的y能看到变化

如果这个答案你想对了,恭喜你,你已经理解了Python赋值的本质。


一张图总结

想象你有一个白板,上面写着一个数字5和一张清单[1,2,3]

不可变对象(整数5)

  • 你贴标签x指向5

  • 你贴标签y也指向5

  • 你让y指向6(新建的)——x还是指向5,不受影响

可变对象(列表[1,2,3])

  • 你贴标签a指向这张清单

  • 你贴标签b也指向同一张清单

  • 你在清单上加了一项4——不管你用a还是b看清单,都能看到4


最后再说一句

我第一次理解“变量是标签”这个概念时,有种豁然开朗的感觉。之前觉得Python的赋值行为很“诡异”,现在觉得它其实很一致、很简单。

所有Python的赋值都是贴标签,没有例外。

  • 整数、字符串、列表、字典、对象——贴的规则都一样

  • 区别在于你贴的对象是否允许被修改

  • 可变对象可以原地改,不可变对象不能

这个理解能帮你少写无数个bug。

下次你再写b = a的时候,心里想的不应该是“把a的值复制给b”,而是“把b也贴到a贴的那个东西上”

就这一念之差,能救你无数次。

http://www.zskr.cn/news/1529260.html

相关文章:

  • 深入解析UART高级功能:本地回环、FIFO模式与错误处理实战
  • RDKit实战:用MolToSmiles标准化SMILES时,别忘了这个参数,否则手性全丢了!
  • DLSS Swapper:3步解锁NVIDIA显卡隐藏性能的终极解决方案
  • PyArrow安装踩坑大全:从手动编译、.whl文件适配到Conda虚拟环境终极方案
  • 终极运动视频分析指南:如何用Kinovea快速提升技术表现 [特殊字符]
  • 2026年厦门企业管理咨询与精益生产转型升级选购指南 - 优质企业观察收录
  • 收藏!小白程序员转型AI大模型工程师的必看指南:高薪风口等你来!
  • 从抓包分析到问题定位:一次完整的Qt5.15 QWebEngine网页加载Timeout排查实录
  • 2026海珠注册公司实操攻略:主城合规流程、片区避坑要点与TOP5代办机构盘点 - 速递信息
  • 并非人人都在事事使用 AI:美国 AI 使用现状与人们的担忧
  • 零绿幕AI背景移除:OBS背景移除插件终极使用指南
  • 2026年浙江杭州合同纠纷律师怎么选?5个关键点防踩雷 - 本地品牌推荐
  • C标准库函数深度解析:内存管理与字符串操作的核心陷阱与最佳实践
  • 如何微调大语言模型以提高可靠性?Awesome-LLM项目中的微调策略详解
  • 如何快速解锁《原神》60帧限制:开源工具完整指南
  • 2026苏州黄金回收高价领跑|合规龙头实测,本地变现避坑全攻略 - 奢侈品回收测评
  • 深度解析:使用RPFM工具构建三国全面战争Startpos文件的实战指南
  • 银联境外支付(线上线下)的储蓄卡和信用卡比较
  • lilos实战项目:从零构建一个多任务LED控制系统
  • 2026年权威橡胶管供应商榜单,靠谱推荐看这篇
  • 手把手教你用8款AI写作辅助软件,极速搞定各类论文
  • 2026年山西企业如何破解获客难题:手机号精准定向、短视频运营与AI搜索优化的完整实战方案 - 优质企业观察收录
  • 【计算机毕业设计案例】基于SpringBoot的校园公共设备维护报修系统设计与实现 高校校园设备运维工单管理系统(程序+文档+讲解+定制)
  • VCSA 6.7证书过期别慌!手把手教你通过SSH修改系统时间恢复登录(附STS证书修复脚本)
  • 仁泽区跑断腿总结:卖黄金遇到这三类店,赶紧走人 - 行行星
  • 中立测评 2026 番禺代账 TOP5,南村电商产业园服务商实地盘点 - 资讯综合站
  • 把 Claude Code 变成你的架构顾问:如何用“隐式重构模式”自动消除代码坏味道
  • Redis - 主从同步与故障切换的常见坑
  • 终极UEFI固件解析指南:5步掌握UEFITool 0.28完整使用教程
  • 深度解析:亨得利原厂配件保修全攻略——2026年最新官方售后网点实测,劳力士欧米茄卡地亚用户必看避坑指南 - 亨得利腕表维修中心