接前面Python源码笔记之内存管理,尝试看看Python的对象的创建与销毁。Python的对象类型还挺多,在Python源码笔记之数据类型中试图列一个表出来,最终未果。
不敢贪多,看4个内建对象。创建对象,也就是创建下面几个结构体的实例了:
结构体 | 通用C API? | Type中的tp_new | |
整数 | PyLongObject | _PyLong_New() | long_new |
字符串 | PyUnicodeObject | _PyUnicode_New() | unicode_new |
列表 | PyListObject | PyList_New | PyType_GenericNew |
字典 | PyDictObject | PyDict_New() | dict_new |
如何创建?
封装的层次太多,太让人迷惑了。简单说,不就是malloc一块合适大小的内存,让PyXxxxObject的指针指向它,为成员赋值么?
如何分配内存:
- 最底层是 C 中的 malloc、realloc、free 这3个东西
- PyMem_{Malloc、Realloc、Free}这3个东西
- PyObject_{Malloc、Free}(内存池) 等
- PyObject_{New、NewVar}等
- _PyObject_GC_{Malloc、New、NewVar、Del}等
幸好,创建内建类型时,我们不需要这些东西,因为它们提供了
- _PyLong_New()
- _PyUnicode_New()
- PyList_New
- PyDict_New()
- ...
以下划线开头的啥意思,看来是不希望大家用的东西,特别是_PyUnicode_New(),直接就是一个static函数。看来要创建一个整数或字符串,需要使用其他的东西了,这就是:
PyLong_FromLong(long ival)
PyLong_FromUnsignedLong(unsigned long ival)
PyLong_FromDouble(double dval)
PyLong_FromVoidPtr(void *p)
PyLong_FromLongLong(PY_LONG_LONG ival)
...
PyObject *PyUnicode_FromString(const char *u)
PyObject *PyUnicode_FromUnicode(const Py_UNICODE *u, Py_ssize_t size)
...
PyXxxx_Type
除了前面这种方式外,Python提供了一种"统一"的创建接口,通过 PyXxxx_Type 中的那些函数指针(fixme)。
PyLong_Type | PyUnicode_Type | PyList_Type | PyDict_Type | |
tp_base | 0 | &PyBaseObject_Type | 0 | 0 |
tp_init | 0 | 0 | list_init | dict_init |
tp_alloc | 0 | 0 | PyType_GenericAlloc | PyType_GenericAlloc |
tp_new | long_new | unicode_new | PyType_GenericNew | dict_new |
tp_free | PyObject_Del | PyObject_Del | PyObject_GC_Del | PyObject_GC_Del |
真复杂,静下心,试着读读看(每个都是从tp_new开始):
- 整数 long_new
这个应该是比较简单,它调用的基本上就是PyLong_From***这些东西
- 字符串 unicode_new
调用基本上就是_PyUnicode_New和PyObject_Str(x)这些
只是它设置了tp_base,不清楚何用。
- PyType_GenericNew
该函数源码如下:
PyObject *
PyType_GenericNew(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
return type->tp_alloc(type, 0);
}
恩,看起来很简单,直接调用了 tp_alloc。也就是PyType_GenericAlloc
源码:
PyObject *
PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)
{
PyObject *obj;
const size_t size = _PyObject_VAR_SIZE(type, nitems+1);
if (PyType_IS_GC(type))
obj = _PyObject_GC_Malloc(size);
else
obj = (PyObject *)PyObject_MALLOC(size);
...
视情况调用_PyObject_GC_Malloc或PyObject_MALLOC,对list,当然是前者喽。
- 字典 dict_new
额,和PyDict_New()类似,只是,还是不清楚tp_alloc干嘛用的。
删除
Python中每一个对象都有一个引用计数。当引用计数为零时,对象将被删除。
Py_REFCNT(ob) | 3个简单的宏,对应结构体3个成员 | |
Py_TYPE(ob) | ||
Py_SIZE(ob) | ||
_Py_NewReference(op) | 将引用计数设置为1 | |
Py_INCREF(op) | 增加、减少引用计数,带X的会判断op是否为空 | |
Py_XINCREF(op) | ||
Py_DECREF(op) | ||
Py_XDECREF(op) | ||
_Py_Dealloc(op) | 引用计数降为零时会被调用,进而调用对象类型中的tp_dealloc |
每一个类型都提供有自己的tp_dealloc,比如:unicode_dealloc、long_dealloc、list_dealloc、dict_dealloc
但尚不清楚这些东西和 tp_free 是什么关系,如何关联的。(要学的东西好多啊)
和删除有关的,应该还有两个问题:
- 缓存?比如小整数始终被缓存起来,永生不灭。但是
static void
long_dealloc(PyObject *v)
{
Py_TYPE(v)->tp_free(v);
}
恩,只要保证引用计数不为0就行了
- PyObject_GC_***
结构体定义
似乎没什么关系哈,不是还是附上
struct _longobject {
PyObject_VAR_HEAD
digit ob_digit[1];
};
typedef struct _longobject PyLongObject;
typedef struct {
PyObject_HEAD
Py_ssize_t length; /* Length of raw Unicode data in buffer */
Py_UNICODE *str; /* Raw Unicode buffer */
Py_hash_t hash; /* Hash value; -1 if not set */
int state; /* != 0 if interned. In this case the two
* references from the dictionary to this object
* are *not* counted in ob_refcnt. */
PyObject *defenc; /* (Default) Encoded version as Python
string, or NULL; this is used for
implementing the buffer protocol */
} PyUnicodeObject;
typedef struct {
PyObject_VAR_HEAD
/* Vector of pointers to list elements. list[0] is ob_item[0], etc. */
PyObject **ob_item;
/* ob_item contains space for 'allocated' elements. The number
* currently in use is ob_size.
* Invariants:
* 0 <= ob_size <= allocated
* len(list) == ob_size
* ob_item == NULL implies ob_size == allocated == 0
* list.sort() temporarily sets allocated to -1 to detect mutations.
*
* Items must normally not be NULL, except during construction when
* the list is not yet visible outside the function that builds it.
*/
Py_ssize_t allocated;
} PyListObject;
typedef struct _dictobject PyDictObject;
struct _dictobject {
PyObject_HEAD
Py_ssize_t ma_fill; /* # Active + # Dummy */
Py_ssize_t ma_used; /* # Active */
/* The table contains ma_mask + 1 slots, and that's a power of 2.
* We store the mask instead of the size because the mask is more
* frequently needed.
*/
Py_ssize_t ma_mask;
/* ma_table points to ma_smalltable for small tables, else to
* additional malloc'ed memory. ma_table is never NULL! This rule
* saves repeated runtime null-tests in the workhorse getitem and
* setitem calls.
*/
PyDictEntry *ma_table;
PyDictEntry *(*ma_lookup)(PyDictObject *mp, PyObject *key, Py_hash_t hash);
PyDictEntry ma_smalltable[PyDict_MINSIZE];
};
object.h中的几个宏
简单罗列一下(只为方便自己查看)
- PyObject/PyVarObject结构体中的成员
#define Py_REFCNT(ob) (((PyObject*)(ob))->ob_refcnt)
#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
#define Py_SIZE(ob) (((PyVarObject*)(ob))->ob_size)
- 1个宏用来将引用计数设置为1。
#define _Py_NewReference(op) (Py_REFCNT(op) = 1)
- 4个宏用来增加/减小 引用计数。(带X的会判断对象指针为否为空)
#define Py_INCREF(op) (((PyObject*)(op))->ob_refcnt++)
#define Py_DECREF(op) \
do { \
if ( --((PyObject*)(op))->ob_refcnt != 0) \
; \
else \
_Py_Dealloc((PyObject *)(op)); \
} while (0)
#define Py_CLEAR(op) \
do { \
if (op) { \
PyObject *_py_tmp = (PyObject *)(op); \
(op) = NULL; \
Py_DECREF(_py_tmp); \
} \
} while (0)
#define Py_XINCREF(op) do { if ((op) == NULL) ; else Py_INCREF(op); } while (0)
#define Py_XDECREF(op) do { if ((op) == NULL) ; else Py_DECREF(op); } while (0)
- 引用计数为零时调用的宏,进而调用对象类型中的 tp_dealloc!
#define _Py_Dealloc(op) ((*Py_TYPE(op)->tp_dealloc)((PyObject *)(op)))