Python中可变(Mutable)与不可变(Immutable)数据结构的内存管理差异
Python可变与不可变数据结构的内存管理差异
先明确两个概念的覆盖范围:
- 不可变数据结构:实例创建后就无法修改内部值的类型,常见的包括
int、str、float、tuple、frozenset - 可变数据结构:实例创建后可随时修改内部值的类型,常见的包括
list、dict、set、大部分自定义类的实例
具体内存管理差异
修改操作的内存行为不同
- 对不可变对象做“修改”操作时,本质是创建了一个全新的对象,原对象的内存地址、存储内容都不会发生变化。
举个实际测试的例子:
这里原来的s = "abc" print(id(s)) # 假设输出 140706732123440 s += "def" print(id(s)) # 输出会变成新的地址,比如 140706732156784"abc"对象并没有被改动,只是新生成了"abcdef"对象重新绑定到变量s上。补充特殊优化:Python对不可变类型有内置的缓存机制,比如[-5, 256]范围内的小整数、符合标识符规则的短字符串会被全局驻留,相同值的这类对象会共用同一块内存,不会重复创建,以此降低内存开销。
- 对可变对象做修改操作时,会直接在原内存块上改动内容,对象的内存地址不会发生变化。
对应例子:
而且可变类型一般会做预分配内存的优化,比如列表会预先申请比实际存储元素更多的内存空间,后续添加元素时不需要每次都重新申请内存,只在预分配空间用完的时候才会触发扩容。lst = [1,2,3] print(id(lst)) # 假设输出 140706731987600 lst.append(4) print(id(lst)) # 输出和之前完全一致
多引用时的行为不同
- 多个变量引用同一个不可变对象时,任意一个变量做修改操作,都只会改变自身的绑定指向,不会影响其他变量指向的原对象,天然不会出现并发修改的问题。
- 多个变量引用同一个可变对象时,任意一个变量对对象做修改,所有引用该对象的变量都会感知到变化,因为所有引用都指向同一块内存地址。
哈希支持的差异
- 不可变对象的值和内存地址固定,所以默认支持哈希计算,
hash()调用可以得到固定的结果,因此可以作为字典的键、也可以存入集合中。 - 可变对象的值可以动态修改,哈希值会随之变化,所以默认不支持哈希,直接调用
hash()会抛出TypeError,也不能作为字典键或者集合元素。
内存回收的效率差异
- 不可变对象的引用计数管理逻辑更简单,只要引用计数归零就可以直接释放内存,不存在部分内容修改需要调整内存块的情况。
- 可变对象因为有预分配的空闲内存,占用的实际内存一般比存储相同内容的不可变类型更高(比如相同元素的list占用内存比tuple高20%~50%),回收时需要额外处理预分配的空闲块,逻辑更复杂。
内容的提问来源于stack exchange,提问作者Welemhret
相关产品推荐
相关产品推荐

