tuple的__sizeof__()比同内容list小,因其仅存储ob_size且无预留空间,而list需存ob_size和allocated并预留空位;例如[1,2,3]占88字节,(1,2,3)仅72字节。

list 和 tuple 在内存分配机制上根本不是“同类实现”,而是两套完全独立的 CPython 底层结构:一个是动态增长的指针数组(PyListObject),另一个是固定长度的紧凑字节数组(PyTupleObject)。这直接导致它们在创建、扩容、访问和 GC 行为上表现迥异。
Python 3.14.2是Python编程语言在2025年12月5日发布的稳定版本,属于3.14系列的第二个维护更新。该版本包含了18项修复,重点解决了多进程、数据类及正则表达式等模块的回归问题,并修复了CVE-2025-12084等安全漏洞。此版本标志着自由线程模式(移除GIL)正式获得官方支持,是Python发展的重要里程碑。
为什么 tuple 的 __sizeof__() 总比同内容 list 小?
因为 tuple 不存“预留空间”和“当前长度”以外的元数据:
- list 必须维护 ob_size(当前元素数)、allocated(已分配槽位数)两个字段,还要为未来 append 预留空位(over-allocation);
- tuple 只存 ob_size,且所有元素指针连续紧排,无冗余间隙。
例如:[1, 2, 3] 占用 88 字节,而 (1, 2, 3) 仅占 72 字节(CPython 3.12 x64)。这不是“省了一点”,而是结构体本身少存两个 ssize_t 字段 + 预留空间。
list.append() 触发的内存重分配 vs tuple + (x,) 的全量拷贝
往 list 末尾追加元素时,CPython 采用几何扩容策略(≈12.5% 增长),多数情况下复用原有内存块;
而任何对 tuple 的“修改”本质都是新建——t + (x,) 会分配一块全新内存,把原 tuple 所有指针 + 新元素指针全部复制进去。
- 10 万次
append到空list:约触发 17 次 realloc,总耗时 ~0.012s - 10 万次
t = t + (x,)构造新tuple:每次全量拷贝,总耗时 ~4.3s(慢 370 倍)
小尺寸 tuple 会被 Python 缓存,list 绝对不会
CPython 对长度 ≤ 20 的 tuple 启用静态缓存池(empty_tuple, singletons 等),相同内容的短 tuple 多次创建会复用同一内存地址:id((1, 2)) == id((1, 2)) 在大多数情况下为 True;
但 id([1, 2]) == id([1, 2]) 永远为 False。
这个优化只作用于不可变对象——因为缓存的前提是“你绝不会改它”。一旦涉及可变性,缓存就变成灾难。
嵌套结构里,tuple 的不可变性不传递
tuple 本身不可变,但它内部的元素可以是可变对象:t = ([1], {'a': 2}) 是合法的,且 t[0].append(3) 不报错;
但你不能执行 t[0] = [] 或 del t[0] —— 报错 TypeError: 'tuple' object doesn't support item assignment。
关键点在于:内存布局上,tuple 存的是指向子对象的指针,它只锁住“指针数组本身”,不锁住指针所指的内容。这点常被误读为“tuple 完全不可变”,实际只是“容器层面不可变”。
真正容易被忽略的,是 tuple 的缓存行为在多线程环境下可能引发隐蔽的引用共享问题——比如两个线程同时拿到同一个缓存 tuple 地址,再各自往其中嵌套的 list 里 append,结果互相污染。这种 bug 不报错,只在数据逻辑上出错。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










