一、先搞懂:核心判断标准(一句话记住)
区分可变类型和不可变类型,只看一个核心点:当变量的值发生改变时,对应的内存地址是否保持不变(用id()函数可查看变量的内存地址,唯一标识一个对象)。
用两个通俗比喻理解:
- 不可变类型:像「一次性饭盒」,饭菜(值)吃完想换,只能重新买一个新饭盒(新内存地址),旧饭盒留着不动;
- 可变类型:像「可重复使用的保鲜盒」,饭菜(值)可以随时更换、添加,不用换盒子(内存地址不变)。
| | |
|---|
| 值改变 → 内存地址改变(创建新对象,原对象不变) | |
| 值改变 → 内存地址不变(修改原对象,不创建新对象) | |
1. 不可变类型:值变,地址必变
这类类型的对象创建后,就像被「封印」了,无法修改内部的值,看似「修改」其实是创建了一个全新的对象,内存地址也会随之更新。
# 示例1:字符串(不可变)s = "hello"print("修改前值:", s)print("修改前地址:", id(s)) # 打印内存地址# 看似修改字符串,实则创建新对象s += " world"print("修改后值:", s)print("修改后地址:", id(s)) # 地址与修改前不同,证明创建了新对象# 示例2:元组(不可变,重点避坑)t = (1, 2, 3)print("\n修改前值:", t)print("修改前地址:", id(t))# 元组的+运算,看似修改,实则创建新元组t += (4, 5)print("修改后值:", t)print("修改后地址:", id(t)) # 地址改变,原元组未被修改
- 元组的「不可变」是指「元素不可修改」,如果元组内包含列表(可变类型),列表内部的值可以修改(地址仍不变);
- 不可变类型的优势:数据安全,不易被意外篡改,可作为字典的键(后续详解)。
2. 可变类型:值变,地址不变
这类类型的对象创建后,内部的值可以自由增删改,修改过程中不会创建新对象,内存地址始终保持不变,直接在原对象上操作。
# 示例1:列表(可变)lst = [1, 2, 3]print("修改前值:", lst)print("修改前地址:", id(lst))# 直接修改原列表(增、删、改均可)lst.append(4) # 末尾添加元素lst[0] = 100 # 修改索引0的元素print("修改后值:", lst)print("修改后地址:", id(lst)) # 地址与修改前相同,证明修改原对象# 示例2:字典(可变)dic = {"name": "Python"}print("\n修改前值:", dic)print("修改前地址:", id(dic))# 直接修改原字典dic["age"] = 30 # 添加键值对dic["name"] = "Java" # 修改已有键的值print("修改后值:", dic)print("修改后地址:", id(dic)) # 地址不变,修改原对象
- 列表的
+=、*=运算会直接修改原列表,地址不变;而lst = lst + [4,5]是创建新列表,地址会改变,日常开发注意区分; - 可变类型的优势:灵活高效,修改数据无需创建新对象,节省内存。
一、先结论:对不可变类型无差异,对可变类型有本质区别
一句话总结核心差异:
- 针对不可变类型(int、float、str、tuple):两者功能完全一致,都是创建新对象,内存地址都会改变;
- 针对可变类型(list、dict、set,重点是列表):两者有本质区别,
a += 1(对应列表a += b)修改原对象,a = a + 1(对应列表a = a + b)创建新对象。
注:a += 1 中的1是数字,仅适用于数字类型(不可变),针对列表等可变类型,常用形式是a += b(b为列表),我们重点围绕「数字类型」和「列表类型」展开,覆盖核心场景。
二、拆解 1:不可变类型(以整数int为例)—— 两者无差异
对于整数、浮点数、字符串等不可变类型,a += 1 和 a = a + 1 都是「先计算新值,再创建新对象,最后让变量a指向新对象」,内存地址都会改变,功能完全一致。
# 不可变类型:inta = 10print("初始值:", a)print("初始地址:", id(a))# 方式1:a = a + 1a1 = a # 保留初始值,用于对比a1 = a1 + 1print("\na = a + 1 后值:", a1)print("a = a + 1 后地址:", id(a1))# 方式2:a += 1a2 = a # 保留初始值,用于对比a2 += 1print("\na += 1 后值:", a2)print("a += 1 后地址:", id(a2))
运算结果:初始值: 10初始地址: 140705267855616a = a + 1 后值: 11a = a + 1 后地址: 140705267855648a += 1 后值: 11a += 1 后地址: 140705267855648
- 不可变类型(int、str 等):
a += 1 与 a = a + 1 无差异,均创建新对象,地址改变; - 可变类型(list 等):
a += b 就地修改原对象(地址不变),a = a + b 创建新对象(地址改变); - 核心逻辑:结合「可变 / 不可变类型」的内存特性,就能轻松理解两者的差异,避免踩坑。