Python变量是对象引用而非存储容器,a = 10表示a指向整数对象10,b = a使b也指向同一对象;不可变对象“修改”实为创建新对象,可变对象就地操作会影响所有引用;函数参数传递本质是对象引用传递,is比较身份(id),==比较值。

变量名只是对象的标签,不是盒子
Python 里没有“变量存储值”这回事。所谓 a = 10,其实是让名字 a 指向内存中一个整数对象 10;b = a 并不是把 10 复制一份给 b,而是让 b 也指向同一个对象。
这解释了为什么对不可变对象(如 int、str、tuple)重复赋值时,看似“修改”了变量,实际是让名字指向了新对象:
a = 10 b = a a += 1 # 等价于 a = a + 1 print(a) # 11 print(b) # 10 —— b 仍指向原来的 int 对象
关键点:
-
id()能看到对象身份:执行id(a)和id(b)在b = a后会返回相同值 - 不可变对象一旦创建就不能改内容,所有“修改”操作都会生成新对象
- 可变对象(如
list、dict)则不同:多个变量引用同一对象时,任一变量调用就地方法(如.append()、.update())会影响所有引用
list 赋值时的浅拷贝陷阱
写 new_list = old_list 不是复制列表,只是多绑一个名字。后续对 new_list 的就地修改(比如 .append()、.pop()、索引赋值)会同步反映在 old_list 上。
立即学习“Python免费学习笔记(深入)”;
常见错误场景:
- 函数接收列表参数后直接修改,意外污染原始数据
- 循环中反复
append()同一个列表引用,结果所有元素都指向同一份数据 - 用
list1 = list2[:]或list1 = list2.copy()可安全做浅拷贝(仅第一层独立)
注意:copy.deepcopy() 才能真正隔离嵌套结构——比如列表里还有字典或子列表时,浅拷贝仍共享内层对象。
函数参数传递本质是“对象引用传参”
Python 没有“传值”或“传引用”的严格分类,参数传递统一是“对象引用的传递”。函数内部能否改变外部变量,取决于该对象是否可变:
- 传入
int/str:函数内重新赋值(如x = x + 1)不影响外部,因为只是让形参指向新对象 - 传入
list/dict:函数内调用.append()、dict[key] = val会改变原对象,因为操作的是同一块内存 - 想避免副作用?函数开头加
data = data.copy()(或data[:]),或文档明确标注“此函数会就地修改”
典型反例:def add_item(lst, item): lst.append(item) —— 这个函数一定会改掉调用方传进来的列表。
is 和 == 的区别暴露了引用机制
== 比较的是值是否相等(调用 __eq__),is 比较的是两个变量是否指向**同一个对象**(即 id(a) == id(b))。
所以:
-
[1,2] == [1,2]是True,但[1,2] is [1,2]是False(两个独立列表对象) -
a = [1,2]; b = a; a is b是True(同对象) - 小整数(-5 到 256)和短字符串会被缓存,所以
100 is 100、"abc" is "abc"偶尔为True,但这属于 CPython 实现细节,不应依赖
判断是否为 None 必须用 is None,因为 None 是单例对象,且自定义类可能重载 == 导致意外行为。










