Python tuple 底层原理1. 整体定位tuple和list都是基于连续指针数组的序列结构,随机访问原理相同,但内存布局的一个关键差异,决定了两者截然不同的性能特征和适用场景:list:对象本身和元素数组是分离的两块内存,靠一层"中间指针"连接,因此可以扩容/增删。tuple:元素数组直接内嵌在对象自身,一次性分配,因此大小和内容结构天生不可变。这一条结构差异是本文档几乎所有结论的根源。2. 底层数据结构// tupletypedefstruct{PyObject_VAR_HEAD PyObject*ob_item[1];// 柔性数组(flexible array member),元素紧跟在对象头之后}PyTupleObject;// 对比:listtypedefstruct{PyObject_VAR_HEAD PyObject**ob_item;// 只是一个指针,指向别处独立分配的内存Py_ssize_t allocated;// 记录那块独立内存的容量}PyListObject;内存布局对比:tuple(一体化,一次性分配): ┌────────────────┬────┬────┬────┐ │ PyObject_VAR_HEAD│ptr │ptr │ptr │ ← 元素数组是对象自身的一部分 └────────────────┴────┴────┴────┘ list(分离式,可替换中间那块内存): ┌─────────────────┬──────┐ │ PyObject_VAR_HEAD │ptr → │──────→ ┌────┬────┬────┬────┐ └─────────────────┴──────┘ │ptr │ptr │ │ │ ← 独立内存,可整体替换 └────┴────┴────┴────┘list 能扩容的原因:扩容时只需要申请一块新的、更大的元素内存,把旧内容拷贝过去,再让ob_item指针重新指向新内存——list 对象本身的地址完全不变,变的只是它内部那个指针指向哪里。tuple 无法扩容的原因:元素数组和对象头是同一块内存、一次性 malloc 出来的,没有"中间指针"可以单独替换。想要改变大小,唯一办法是整体重新分配一个新对象——这已经不是"修改原tuple",而是"创建了一个新tuple"。3. 不可变性的真正实现机制3.1 不是靠 C 语言的constconst是编译期类型检查,但PyTupleObject的定义完全没有用const修饰——因为创建阶段必须能写入初始元素(比如(1, 2, 3)构造时需要把三个指针写进ob_item),如果内存是const,连初始化都做不到。底层内存本身是可写的,const这条路从设计上就不适用。3.2 真正机制:类型对象里根本不注册"修改"接口CPython 每种类型都有一个PyTypeObject,里面挂着描述"这个类型支持哪些操作"的函数指针表(PySequenceMethods等)。list的类型对象:赋值操作对应的函数指针指向真实实现(list_ass_item)tuple的类型对象:对应位置是空指针(0),根本没有实现