当前位置:首页>python>Python 内存分析完全指南(上):不可变与可变对象

Python 内存分析完全指南(上):不可变与可变对象

  • 2026-09-02 16:04:36
Python 内存分析完全指南(上):不可变与可变对象

理解 Python 的内存模型,比死记“传值还是传引用”更有用。变量名保存在栈侧,真正的对象在堆上;赋值改的是“指向谁”,就地修改改的是“对象内部”。本篇先建立心智模型,再对比不可变对象与可变对象的内存行为。

本篇目录

1. 先建立心智模型

2. 不可变对象的内存行为

3. 可变对象的内存行为

1. 先建立心智模型

Python 中几乎一切都是对象。观察一个对象,通常看三件事:

维度
含义
常用查看方式
身份 identity
对象在一次运行中的唯一标识
id(obj)
类型 type
对象支持哪些操作
type(obj)
值 value
对象表达的数据内容
直接打印或访问属性

教学图里常把内存画成:

• 栈:保存变量名,以及它当前指向的对象标识

• 堆:保存真正的对象(类型、值、内部引用)

变量更像标签,不是“装满数据的盒子”。a = b 的含义是:让 a 与 b 指向同一个对象,而不是复制一份新的内容。

注意:id() 的具体数值只在当前进程、当前时刻有意义,不同运行会变化;下文配图中的地址是示意编号,不要当成固定常量。

2. 不可变对象的内存行为

2.1 哪些对象是不可变的

不可变对象不能在原地改内容;“修改”通常意味着创建新对象,再让变量指向新对象。

常见不可变类型:

• int

• float

• bool

• str

• tuple

2.2 赋值:变量绑定到对象

name = "lili"print("内存地址", id(name))print("类型", type(name))print("值", name)

执行后,栈上的 name 保存一个引用,堆上存在一个字符串对象,包含 id、type=str、value='lili'。

2.3 重新赋值:旧对象不再被当前变量引用

name = "lili"name = "feifei"

第二次赋值不会改写 'lili' 本身,而是:

1. 在堆上创建新的字符串对象 'feifei'

2. 让 name 指向新对象

3. 原来的 'lili' 若没有其他引用,后续可由垃圾回收回收

可用代码验证“换了对象”:

name = "lili"old_id = id(name)name = "feifei"print(old_id == id(name))  # False

2.4 别名:多个变量指向同一不可变对象

name = "lili"name2 = nameprint(name is name2)  # True

name2 = name 不会复制字符串,只是再贴一张标签。两个变量指向同一个堆对象。

因为对象不可变,通过任一别名都无法改坏另一个变量“眼里的内容”;真正危险的是可变对象的别名共享。

3. 可变对象的内存行为

3.1 哪些对象是可变的

可变对象允许在保持对象身份不变的前提下,改写内部数据。

常见可变类型:

1. list

2. dict

3. set

3.2 创建列表

lst = [23, 56, 78]

栈上的 lst 指向堆上的列表对象。列表内部再保存对各个元素对象的引用。

3.3 就地修改:地址通常不变

lst = [23, 56, 78]before = id(lst)lst[1] = 66after = id(lst)print(before == after)  # Trueprint(lst)              # [23, 66, 78]

lst[1] = 66 改的是列表内部第 1 个位置指向谁,列表对象本身的身份不变。因此:

• id(lst) 通常保持不变

• 所有指向该列表的别名,都会立刻看到新内容

对比一下“重新绑定”:

lst = [23, 56, 78]lst = [23, 66, 78]  # 这是换了一个全新的列表对象

前者是原地改;后者是让变量指向另一个列表。

操作
是否新建容器
常见效果
lst[i] = x
 / append / update
否
身份不变,别名共享变化
lst = [...]
 / lst = lst + [...]
是
身份变化,原对象是否存活取决于还有没有引用

最新文章

随机文章