对象分类
对象三要素 对象三要素:
PyObject定义如下
1 2 3 4 5 6 7 8 9 10 // Include/object.h #define _PyObject_HEAD_EXTRA \ struct _object *_ob_next; \ struct _object *_ob_prev; typedef struct _object { _PyObject_HEAD_EXTRA // 双向链表 垃圾回收 需要用到 Py_ssize_t ob_refcnt; // 引用计数 struct _typeobject *ob_type; // 指向类型对象的指针,决定了对象的类型 } PyObject;
定长与变长对象 1 2 3 4 5 6 7 8 9 10 11 12 // Include/object.h typedef struct _object { _PyObject_HEAD_EXTRA Py_ssize_t ob_refcnt; struct _typeobject *ob_type; } PyObject; typedef struct { PyObject ob_base; Py_ssize_t ob_size; /* Number of items in variable part */ } PyVarObject; /* PyVarObject比PyObject多出了一个用于存储元素个数的变量ob_size */
类型对象 PyObject 的 对象类型指针struct _typeobject *ob_type, 它指向的类型对象就决定了一个对象是什么类型的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 typedef 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; printfunc tp_print; getattrfunc tp_getattr; setattrfunc tp_setattr; PyAsyncMethods *tp_as_async; /* formerly known as tp_compare (Python 2) or tp_reserved (Python 3) */ reprfunc tp_repr; /* Method suites for standard classes */ // 标准类方法集 PyNumberMethods *tp_as_number; // 数值对象操作 PySequenceMethods *tp_as_sequence; // 序列对象操作 PyMappingMethods *tp_as_mapping; // 字典对象操作 // 更多标准操作 /* More standard operations (here for binary compatibility) */ hashfunc tp_hash; ternaryfunc tp_call; reprfunc tp_str; getattrofunc tp_getattro; setattrofunc tp_setattro; ...... } PyTypeObject;
Python 中的所有对象都拥有一些相同的内容,而这些内容就定义在PyObject中。
类型对象创建 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 // Include/object.h #ifdef Py_TRACE_REFS #define _PyObject_EXTRA_INIT 0, 0, #else #define _PyObject_EXTRA_INIT #endif #define PyObject_HEAD_INIT(type) \ { _PyObject_EXTRA_INIT \ 1, type }, // Objects/longobject.c static PyNumberMethods long_as_number = { (binaryfunc)long_add, /*nb_add*/ (binaryfunc)long_sub, /*nb_subtract*/ (binaryfunc)long_mul, /*nb_multiply*/ ...... }; PyTypeObject PyLong_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "int", /* tp_name */ offsetof(PyLongObject, ob_digit), /* tp_basicsize */ sizeof(digit), /* tp_itemsize */ long_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ long_to_decimal_string, /* tp_repr */ &long_as_number, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ ...... };
对象创建
1.这类 API 通常形如PyObject_XXX这样的形式。可以应用在任何 Python 对象上, 如PyObject_New。创建一个整数对象的方式1 PyObject* longobj = PyObject_New(Pyobject, &PyLong_Type);
2.与类型相关的 API 或称为 COL (Concrete Object Layer) 这类 API 通常只能作用于某一种类型的对象上,对于每一种内建对象 Python 都提供了这样一组 API。例如整数对象,我们可以利用如下的 API 创建1 PyObject *longObj = PyLong_FromLong(10);
对象的多态性 Python 创建一个对象比如 PyLongObject 时,会分配内存进行初始化, 然后 Python 内部会用 PyObject* 变量来维护这个对象,其他对象也与此类似 所以在 Python 内部各个函数之间传递的都是一种范型指针 PyObject* 我们不 知道这个指针所指的对象是什么类型,只能通过所指对象的 ob_type 域 动态进 行判断,而 Python 正是通过 ob_type 实现了多态机制 考虑以下的 calc_hash 函数
1 2 3 4 5 6 Py_hash_t calc_hash(PyObject* object) { Py_hash_t hash = object->ob_type->tp_hash(object); return hash; }
如果传递给 calc_hash 函数的指针是一个 PyLongObject*,那么它会调用 PyLongObject 对象对应的类型对象中定义的 hash 操作tp_hash,tp_hash可以在PyTypeObject中找到, 而具体赋值绑定我们可以在 PyLong_Type 初始化代码中看到绑定的是long_hash函数
1 2 3 4 5 6 7 8 9 10 // Objects/longobject.c PyTypeObject PyLong_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "int", /* tp_name */ ... (hashfunc)long_hash, /* tp_hash */ ... };
如果指针是一个 PyUnicodeObject*,那么就会调用 PyUnicodeObject 对象对应的类型对象中定义的 hash 操作,查看源码可以看到 实际绑定的是 unicode_hash函数
1 2 3 4 5 6 7 8 9 10 11 // Objects/unicodeobject.c PyTypeObject PyUnicode_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "str", /* tp_name */ ... (hashfunc) unicode_hash, /* tp_hash*/ ... };
引用计数 Python 通过引用计数来管理维护对象在内存中的存在与否 Python 中的每个东西都是一个对象, 都有ob_refcnt 变量, 这个变量维护对象的引用计数,从而最终决定该对象的创建与销毁 在 Python 中,主要通过 Py_INCREF(op)与Py_DECREF(op) 这两个宏 来增加和减少对一个对象的引用计数。当一个对象的引用计数减少到 0 之后, Py_DECREF将调用该对象的tp_dealloc来释放对象所占用的内存和系统资源; 但这并不意味着最终一定会调用 free 释放内存空间。因为频繁的申请、释放 内存会大大降低 Python 的执行效率。因此 Python 中大量采用了内存对象 池的技术,使得对象释放的空间归还给内存池而不是直接free,后续使用可先 从对象池中获取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // Include/object.h #define _Py_NewReference(op) ( \ _Py_INC_TPALLOCS(op) _Py_COUNT_ALLOCS_COMMA \ _Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA \ Py_REFCNT(op) = 1) #define Py_INCREF(op) ( \ _Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA \ ((PyObject *)(op))->ob_refcnt++) #define Py_DECREF(op) \ do { \ PyObject *_py_decref_tmp = (PyObject *)(op); \ if (_Py_DEC_REFTOTAL _Py_REF_DEBUG_COMMA \ --(_py_decref_tmp)->ob_refcnt != 0) \ _Py_CHECK_REFCNT(_py_decref_tmp) \ else \ _Py_Dealloc(_py_decref_tmp); \ } while (0)