1.1. Python内的对象
Python中的类和实例都是通过Python内的对象来实现的。Python中已经预先定义了一些类型对象。这些内建类型对象通过实例化,可以创建内建类型对象的实例对象。
在Python中,对象就是为C中的结构体在堆上申请的一块内存,一般来说,对象是不能被静态初始化的,并且也不能在栈空间上生存。唯一的例外就是类型对象,Python中所有的内建的类型对象都是被静态初始化的。
在Python中,一个对象一旦被创建,它在内存中的大小就是不变的了。这就意味着那些需要容纳可变长度数据的对象只能在对象内维护一个指向一块可变大小的内存区域的指针。
1.2. PyObject
PyObject是整个Python对象机制的核心,定义如下:
// object.h
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
其中_PyObject_HEAD_EXTRA定义如下:
// object.h
#ifdef Py_TRACE_REFS
#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
可以看到release编译时不会定义Py_TRACE_REFS。
PyObject类中ob_refcnt与内存引用计数相关,ob_type用来指定一个对象类型的类型对象。在Python中,对象机制的核心一个是引用计数,一个就是类型信息。
每一个Python对象除了必须有这个PyObject内容外,还应该占有一些额外的内存,放置些其他的东西。比如float对象除了PyObject,还有一个额外的double变量,如下定义:
// object.h
#define PyObject_HEAD PyObject ob_base;
1.3. PyVarObject
把浮点对象这样不包含可变长度数据的对象称为“定长对象”,而字符串对象这样包含可变长度数据的对象称为“变长对象”,它们的区别在于定长对象的不同对象占用的内存大小是一样的,而变长对象的不同对象占用的内存可能是不一样的。
表示变长对象的结构体PyVarObject定义如下:
// object.h
typedef struct {
PyObject ob_base;
Py_ssize_t ob_size;
} PyVarObject;
每一个可变Python对象除了有PyVarObject内容外,还占有一些额外的内存,放置些其他的东西。比如list对象为变长对象,它的结构体如下:
// listobject.h
typedef struct {
PyObject_VAR_HEAD
PyObject **ob_item;
Py_ssize_t allocated;
} PyListObject;
其中PyObject_VAR_HEAD定义了PyVarObject类型对象,如下:
// object.h
#define PyObject_VAR_HEAD PyVarObject ob_base;
1.4. PyTypeObject
对象对应的类型对象定义如下:
// object.h
typedef struct _typeobject {
PyObject_VAR_HEAD
const char *tp_name;
Py_ssize_t tp_basicsize, tp_itemsize;
destructor tp_dealloc;
printfunc tp_print;
// ...
} PyTypeObject;
定义中包含了许多信息,主要分为4类:
- 类型名,tp_name,主要是Python内部以及调试的时候使用;
- 创建该类型对象时分配内存空间大小的信息,即tp_basicsize和tp_itemsize;
- 与该类型对象相关联的操作信息,诸如tp_print这样的函数;
- 类型的类型信息;
在PyTypeObject中定义了大量的函数指针,这些函数指针最终都会指向某个函数,或者指向NULL,可以视为类型对象中所定义的操作。在这些操作信息中,有三组非常重要的操作族:tp_as_number,tp_as_sequence和tp_as_mapping,分别指向PyNumberMethods,PySequenceMethods和PyMappingMethods函数族。PyNumberMethods、PySequenceMethods、PyMappingMethods分别定义了作为一个数值对象、序列对象和关联对象应该支持的操作。
类型对象的类型是PyType_Type:
PyTypeObject PyType_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"type",
sizeof(PyHeapTypeObject),
sizeof(PyMemberDef),
(destructor)type_dealloc,
0,
0,
0,
0,
(reprfunc)type_repr,
0,
0,
0,
0,
(ternaryfunc)type_call,
0,
(getattrofunc)type_getattro,
(setattrofunc)type_setattro,
0,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_BASETYPE | Py_TPFLAGS_TYPE_SUBCLASS,
type_doc,
(traverseproc)type_traverse,
(inquiry)type_clear,
0,
offsetof(PyTypeObject, tp_weaklist),
0,
0,
type_methods,
type_members,
type_getsets,
0,
0,
0,
0,
offsetof(PyTypeObject, tp_dict),
type_init,
0,
type_new,
PyObject_GC_Del,
(inquiry)type_is_gc,
};
1.5. Python对外提供的C API
Python的C API分为两类,一类称为范性的API(AOL,Abstract Object Layer),这类API都具有诸如PyObject_*的形式,可以应用在任何Python对象上;另一类是与类型相关的API(COL,Concrete Object Layer),这类API通常只能作用在某一种类型的对象上,对于每一种内建对象,Python都提供了这样的一组API。
1.6 参考
- Python源码剖析
本文作者:whj0709
阅读原文
本文为云栖社区原创内容,未经允许不得转载。