42
、True
、len
本身)。int
, double
, boolean
等 非对象 的原始类型(primitive)。Object[]
,必须用装箱类(Integer
, Double
)才能享受对象待遇。实现层面
[1,2]
、函数 len
、类 int
本身……PyObject
(含引用计数 + 类型指针),后面爱加多少字段再加。PyObject *
安全地传递。PyObject
里?PyObject
里?type(x)
必须能在运行时回答。ob_type
指针塞进头部,解释器就能:ob_type
)ob_type->tp_methods
等)obj->ob_type->tp_repr(obj)
)PyObject
就是 CPython 的“对象通行证”:少了它,解释器连怎么分配、怎么回收、怎么找方法都无从下手。CPython 源码中,PyObject *
是一个指向对象结构体的指针,它是 Python 所有对象的“最小公共祖先”:
typedef struct _object {
Py_ssize_t ob_refcnt; // 引用计数
struct _typeobject *ob_type; // 类型指针
} PyObject; 外只暴露 PyObject
这个名字,隐藏底层 struct _object,
struct _object
是结构体的标签名(tag)只在结构体内部可见
PyObject
是类型别名(typedef 名),对外使用。 使用时直接写 PyObject obj;不用写struct _object obj
ob_refcnt
:引用计数,用于垃圾回收。ob_type
:指向类型对象的指针(也就是你熟悉的 Python 的 type(obj)
)。继承 PyObject 的对象长啥样?
比如一个自定义对象: c typedef struct { PyObject_HEAD // 展开后是 PyObject 的成员 int my_value; } MyObject;
MyObject
的第一个成员就是 PyObject
,所以你可以把 MyObject*
当作 PyObject*
来用。在 C 语言里谈“继承”,本质上就是把父结构体完整地嵌(放)在子结构体的最前面,除此之外没有别的魔法—没有 extends
,没有自动向上转型,也没有虚函数表,全靠“内存布局”+“强制类型转换”来手动实现。C 语言本身并没有“继承、多态”这两个语法概念
PyObject
(或变长版 PyVarObject
)对象内存布局 = [ PyObject 头部 | 子类私有字段 ]
PyObject *
,不是二级指针PyDictObject
,键是字符串,值是 PyObject *
PyDictObject
,3.11 起有了更紧凑的 PyLocalsArray
,但同样存的是 PyObject *
__dict__
同理,值都是 8 字节(64 位机)指针。locals["lst"] -----> PyListObject 在堆上的地址
Python 层: lst = [1, 2, 3] C 层: locals (PyDictObject) +---------+ | key | value (PyObject *) | "lst" | 0x7f8e3c2a1b00 ----------> [PyObject_HEAD | ob_size=3 | *ob_item] +---------+ 没有 PyObject **; 没有“二级指针”; 变量名 "lst" 只是字典键,指向唯一的 PyListObject。 “所有对象都继承自 PyObject,变量名只是给指向堆对象的指针贴的标签。PyListObject
里面又是一块连续的“一级指针”数组,专门用来存放列表元素