1.2 Python3 对象深入分析
对象的基石,PyObject
上一章节探讨了python一切皆对象的由来。在python中,对象可分为类型对象和实例对象,而根据不同的对象特性,又可细分为可变、不可变、定长、不定长等。
Python是由C语言实现的(Python虚拟机是C语言编写的,可以直接调用C语言函数。很多内建对象是由C语言编写的,模块对象保存了这些C函数的指针。当Python调用这些对象时,CALL_FUNCTION字节码会找到这些函数指针并调用它们),那么对象在Python内部是如何实现的?
在Python内部,所有对象均是由PyObject结构体表示,对象引用则是指针PyObject*。在Python源码include/object.h中,定义了PyObject结构体。(以Python3.10.0为例)
/* Nothing is actually declared to be a PyObject, but every pointer to
* a Python object can be cast to a PyObject*. This is inheritance built
* by hand. Similarly every pointer to a variable-size Python object can,
* in addition, be cast to PyVarObject*.
*/
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
PyTypeObject *ob_type;
} PyObject;
以上代码定义了一个结构体_object以及结构体变量PyObject。
首先看结构体的成员_PyObject_HEAD_EXTRA宏。
#ifdef Py_TRACE_REFS
/* Define pointers to support a doubly-linked list of all live heap objects. */
#define _PyObject_HEAD_EXTRA \
struct _object *_ob_next; \
struct _object *_ob_prev;
#define _PyObject_EXTRA_INIT 0, 0,
#else
# define _PyObject_HEAD_EXTRA
# define _PyObject_EXTRA_INIT
#endif
根据注释和代码解读,可以了解当Py_TRACES_REFS有定义,则将宏定义为两个指针*_ob_next和*_ob_prev。这两个指针实现了双向链表,用于跟踪所有活跃堆对象。通常情况下不启用,这里不做深入了解。
PyObject结构体中,还包含两个成员:
- 引用计数(ob_refcnt):对象被其他地方引用时加一,引用解除时减一,当引用计数为零,便可将对象回收(垃圾回收的主要机制之一)。
- 类型指针(ob_type):指向对象的类型对象,类型对象描述实例对象的数据和行为。
继续往下阅读object.h代码,可以看到结构体变量PyVarObject。
typedef struct {
PyObject ob_base;
Py_ssize_t ob_size; /* Number of items in variable part */
} PyVarObject;
这是Python变长对象所对应的C结构体,在原先的PyObject基础上加入了长度信息ob_size,用于记录元素的个数。
在对象中,定长与变长是二选一的属性,如果不是定长就是变长,也就是说在对象的结构体中,需要包含头部PyObject或PyvarObject。为此,头文件特地准备了两个宏定义,分别表示定长宏与不定长宏:
#define PyObject_HEAD PyObject ob_base;
#define PyObject_VAR_HEAD PyVarObject ob_base;
各对象结构组成可概况如下:

以大小固定的浮点对象而言,只需要在PyObject头部基础上,添加一个item即可,浮点对象的item使用一个双精度浮点数double加以实现。
typedef struct {
PyObject_HEAD
double ob_fval;
} PyFloatObject;

而对于变长的列表对象而言,则需要一个PyVarObject以及动态数组。
typedef struct {
PyObject_VAR_HEAD
PyObject **ob_item;
Py_ssize_t allocated;
} PyListObject;

其中三个关键字段含义如下:
- ob_item,指向动态数组的指针,数组保存元素对象指针;
- allocated,动态数组总长度,表示当前列表的容量,在Python中,如果列表容量不足时,会自动扩容,这个需要与列表实际的元素数量区分开;
- ob_size,当前列表中的元素个数,即列表当前的长度(len(ob))。
类型的基石,PyTypeObject
从对象的基础结构体PyObject,可以了解所有对象的共有信息。对于内存中的任一对象,不管是何类型,肯定会存在 引用计数、类型指针 以及变长对象特有的 元素个数。
但以我们对Python对象的了解,PyObject无法解决以下问题:
- 不同类型的对象所需内存空间不同,创建对象时从哪里得知内存信息?
- 对于给定对象,怎么判断它支持什么操作?
事实上,这些信息称为对象的元信息,由一个独立实体保存,与对象所属类型密切相关。还记得PyObject内包含一个指针ob_type吗?该指针指向了一个类型对象,该类型对象便是PyTypeObject。
PyTypeObject的代码在cpython/object.h文件中,部分代码如下:
struct _typeobject {
PyObject_VAR_HEAD
const char *tp_name; /* For printing, in format "<module>.<name>" */
Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */
/* Methods to implement standard operations */
destructor tp_dealloc;
Py_ssize_t tp_vectorcall_offset;
getattrfunc tp_getattr;
setattrfunc tp_setattr;
// ...
// Strong reference on a heap type, borrowed reference on a static type
struct _typeobject *tp_base;
// ...
};
重要字段如下:
- PyObject_VAR_HEAD:类型对象是一个变长对象;
- 类型名称:tp_name字段;
- 类型的继承信息,例如tp_base字段指向基类对象;
- 创建实例对象所需要的内存信息,即tp_basicsize和tp_itemsize字段;
- 该类型支持的相关操作信息,即tp_getattr、tp_setattr等函数指针;
以浮点为例,通过is关键字判断类型对象和实例对象在内存中(id)的形态和关系:
>>> float
<class 'float'>
>>> pi = 3.14
>>> e = 2.71
>>> type(pi) is float
True
>>> type(e) is float
True
float为浮点类型对象,系统中是唯一的,保存了所有浮点实例对象的元信息。所以实例对象pi和e的type均是float对象。
各对象的关系在内存中的形式如下:

如图可见,两个浮点实例对象都是PyFloatObject结构体,除了公共头部字段ob_refcnt和ob_type,专有字段ob_val保存了对应的数值。其中ob_type字段指向了float的类型对象(也是一个PyTyoeObject的结构体),这里保存了类型名、内存分配信息以及浮点相关操作等。Python便是依据对象类型,进而得知对象元信息。注意,这里float、pi以及e等变量只是一个指向实际对象的指针。
浮点类型对象是全局唯一的,在C语言层面上作为一个全局变量静态定义即可。浮点类型对象PyFloat_Type代码位于Object/floatobject.c文件中:
PyTypeObject PyFloat_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"float",
sizeof(PyFloatObject),
0,
(destructor)float_dealloc, /* tp_dealloc */
// ...
(reprfunc)float_repr, /* tp_repr */
// ...
};
在PyFloat_Type结构体中,第二行初始化了ob_refcnt、ob_type以及ob_size三个字段;第三行将tp_name字段初始化成类型名称float;再往下就是各种操作的函数指针。
需要注意到第二行中ob_type指针指向PyType_Type,这也是一个静态定义的全局变量。由此可见,代表“类型的类型”即type的那个对象应该就是PyType_Type了。
类型的类型,PyType_Type
上一节了解了,所有对象的元类型都是type,这里的PyType_Type的就是所说的type的结构体。其代码在Object/typeobject.c中:
PyTypeObject PyType_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"type", /* tp_name */
sizeof(PyHeapTypeObject), /* tp_basicsize */
sizeof(PyMemberDef), /* tp_itemsize */
(destructor)type_dealloc, /* tp_dealloc */
// ...
};
内建类型和自定义类对应的PyTypeObject对象都是通过这个PyType_Type创建的。PyType_Type在Python的类型机制中是一个非常重要的对象,是所有类型的类型,称为元类型(meta class)。

所有的类型对象,包括内建类型、自定义类还有元类,本质上都是类型对象。底层结构是类型的,内存结构均由PyTypeObject结构体定义。以自定义类型为例,调用type(PyType_Type),type将为新的自定义类分配内存(PyTypeObject结构体)并初始化相关字段;以内建对象为例,由于内建对象都是静态定义,type无须为其分配内存,但仍负责字段的初始化。
类型之基,PyBaseObject_Type
在文件Object/typeobject.c中,可以查看PyBaseObject_Type的代码:
PyTypeObject PyBaseObject_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"object", /* tp_name */
sizeof(PyObject), /* tp_basicsize */
// ....
};
根据第二行的代码,object的类型也是type,此外,object就是继承链的末端,没有设置具体的tp_base值。
>>> object.__class__
<class 'type'>
>>> object.__base__
>>>
至此,我们大致清楚了Python对象体系中所有实体以及关系。

浙公网安备 33010602011771号