博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python 源码剖析(四)【LIST对象】
阅读量:7000 次
发布时间:2019-06-27

本文共 8219 字,大约阅读时间需要 27 分钟。

四、LIST对象

1、PyListObject对象

2、PyListObject的创建与维护

3、PyListObject 对象缓冲池

4、Hack PyListObject


1、PyListObject对象

PyListObject 对象是变长对象,而且还是一个可变对象:

[listobject.h] typedef struct {    PyObject_VAR_HEAD    /* Vector of pointers to list elements.  list[0] is ob_item[0], etc. */    PyObject **ob_item;    int allocated;} PyListObject;
PyObject_VAR_HEAD 中有一个ob_size和allocated,allocated 指申请了内存的大小,ob_size指使用内存的大小,0<=ob_size<=allcated。

 

2、PyListObject的创建与维护

2.1、创建

唯一创建方法PyList_New:

listobject.c] PyObject* PyList_New(int size){    PyListObject *op;    size_t nbytes;     nbytes = size * sizeof(PyObject *);    /* Check for overflow */    if (nbytes / sizeof(PyObject *) != (size_t)size)        return PyErr_NoMemory();     //为PyListObject申请空间    if (num_free_lists) {        //使用缓冲池        num_free_lists--;        op = free_lists[num_free_lists];        _Py_NewReference((PyObject *)op);   } else {        //缓冲池中没有可用的对象,创建对象        op = PyObject_GC_New(PyListObject, &PyList_Type);    }    //为PyListObject对象中维护的元素列表申请空间    if (size <= 0)        op->ob_item = NULL;    else {        op->ob_item = (PyObject **) PyMem_MALLOC(nbytes);        memset(op->ob_item, 0, nbytes);    }    op->ob_size = size;    op->allocated = size;    _PyObject_GC_TRACK(op);    return (PyObject *) op;}

先进行检查,再判断缓冲池是否可用,不可则malloc在堆上新建PyListObject。PyListObject缓冲池为free_lists:

/* Empty list reuse scheme to save calls to malloc and free */ #define MAXFREELISTS 80 static PyListObject *free_lists[MAXFREELISTS]; static int num_free_lists = 0;

 

放元素到List指定位置:

[listobject.c] int PyList_SetItem(register PyObject *op, register int i, register PyObject   *newitem){    register PyObject *olditem;    register PyObject **p;    if (!PyList_Check(op)) {        ……    }    if (i < 0 || i >= ((PyListObject *)op) -> ob_size) {        Py_XDECREF(newitem);        PyErr_SetString(PyExc_IndexError,                "list assignment index out of range");        return -1;    }    p = ((PyListObject *)op) -> ob_item + i;    olditem = *p;    *p = newitem;    Py_XDECREF(olditem);    return 0;}

 

2.2、添加

插入元素:

[listobject.c] int PyList_Insert(PyObject *op, int where, PyObject *newitem){    ......//类型检查    return ins1((PyListObject *)op, where, newitem);}
static int ins1(PyListObject *self, int where, PyObject *v){    int i, n = self->ob_size;    PyObject **items;    ......    if (list_resize(self, n+1) == -1)        return -1;    if (where < 0) {        where += n;        if (where < 0)            where = 0;    }    if (where > n)        where = n;    items = self->ob_item;    for (i = n; --i >= where; )        items[i+1] = items[i];    Py_INCREF(v);    items[where] = v;    return 0;}

看看list_resize:

listobject.c]static int list_resize(PyListObject *self, int newsize){    PyObject **items;    size_t new_allocated;    int allocated = self->allocated;     /* Bypass realloc() when a previous overallocation is large enough       to accommodate the newsize.  If the newsize falls lower than half       the allocated size, then proceed with the realloc() to shrink the list.    */    if (allocated >= newsize && newsize >= (allocated >> 1)) {        assert(self->ob_item != NULL || newsize == 0);        self->ob_size = newsize;        return 0;    }     /* This over-allocates proportional to the list size, making room     * for additional growth.  The over-allocation is mild, but is     * enough to give linear-time amortized behavior over a long     * sequence of appends() in the presence of a poorly-performing     * system realloc().     * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...     */    new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;    if (newsize == 0)        new_allocated = 0;    items = self->ob_item;    if (new_allocated <= ((~(size_t)0) / sizeof(PyObject *)))        PyMem_RESIZE(items, PyObject *, new_allocated);    else        items = NULL;    if (items == NULL) {        PyErr_NoMemory();        return -1;    }    self->ob_item = items;    self->ob_size = newsize;    self->allocated = new_allocated;    return 0;}

插入的时候,Python分四种情况处理:

1.      newsize > ob_size && newsize < allocated :简单调整ob_size值。

2.      newsize < ob_size && newsize > allocated/2 :简单调整ob_size值。

3.      newsize < ob_size && newsize < allocated/2 :调用realloc,重新分配空间。

4.      newsize > ob_size && newsize > allocated :调用realloc,重新分配空间。

调整完空间后开始插入元素,其中体现负值索引的特性:

static int ins1(PyListObject *self, int where, PyObject *v){    ......    if (where < 0) {        where += n;        if (where < 0)            where = 0;    }    if (where > n)        where = n;    items = self->ob_item;    for (i = n; --i >= where; )        items[i+1] = items[i];    Py_INCREF(v);    items[where] = v;    return 0;}

PyListObject 与 C++中的vector相似。

再看看list中的append:

[listobject.c]int PyList_Append(PyObject *op, PyObject *newitem){    if (PyList_Check(op) && (newitem != NULL))        return app1((PyListObject *)op, newitem);    PyErr_BadInternalCall();    return -1;} static PyObject* listappend(PyListObject *self, PyObject *v){    if (app1(self, v) == 0)        Py_RETURN_NONE;    return NULL;} static int app1(PyListObject *self, PyObject *v){    int n = PyList_GET_SIZE(self);......    if (list_resize(self, n+1) == -1)        return -1;     Py_INCREF(v);    PyList_SET_ITEM(self, n, v);    return 0;}

添加的元素添加在ob_size位置,而不是allocated位置上。

 

2.3、删除

PyListObject的删除remove:

[listobject.c] static PyObject * listremove(PyListObject *self, PyObject *v){    int i;    for (i = 0; i < self->ob_size; i++) {        int cmp = PyObject_RichCompareBool(self->ob_item[i], v, Py_EQ);        if (cmp > 0) {            if (list_ass_slice(self, i, i+1,(PyObject *)NULL) == 0)                Py_RETURN_NONE;            return NULL;        }        else if (cmp < 0)            return NULL;    }    PyErr_SetString(PyExc_ValueError, "list.remove(x): x not in list");    return NULL;}

先用PyObject_RichCompareBool匹配元素是否在list上,在的话用list_ass_slice 删除:

[listobject.c]static int list_ass_slice(PyListObject *a, int ilow, int ihigh, PyObject *v){   PyObject *recycle_on_stack[8];    PyObject **recycle = recycle_on_stack; /* will allocate more if needed */     PyObject **item;    int n; /* # of elements in replacement list */    int norig; /* # of elements in list getting replaced */    int d; /* Change in size */#define b ((PyListObject *)v)    if (v == NULL)        n = 0;else {……}     norig = ihigh - ilow;    d = n - norig;item = a->ob_item;    //[1]     s = norig * sizeof(PyObject *);     if (s > sizeof(recycle_on_stack)) {         recycle = (PyObject **)PyMem_MALLOC(s);         if (recycle == NULL) {             PyErr_NoMemory();             goto Error;         }     }     memcpy(recycle, &item[ilow], s);  //[2]     if (d < 0) { /* Delete -d items */        memmove(&item[ihigh+d], &item[ihigh],(a->ob_size - ihigh)*sizeof(PyObject *));        list_resize(a, a->ob_size + d);        item = a->ob_item;    }……//[3] for (k = norig - 1; k >= 0; --k)         Py_XDECREF(recycle[k]);#undef b}

当v为NULL时执行删除动作,删除个数为ihigh-ilow=1,最后通过memove执行删除元素。PyListObject删除元素时会引起内存搬移动作。

list_ass_slice不仅仅是用做删除元素,它还可以进行插入元素的动作:

a[ilow:ihigh] = v if v != NULL.

del a[ilow:ihigh] if v == NULL. 

 


 

3、PyListObject 对象缓冲池

刚刚提到的free_lists是在PyListObject销毁时产生的:

[listobject.c] static void list_dealloc(PyListObject *op){    int i;    PyObject_GC_UnTrack(op);    Py_TRASHCAN_SAFE_BEGIN(op)    if (op->ob_item != NULL) {        /* Do it backwards, for Christian Tismer.           There's a simple test case where somehow this reduces           thrashing when a *very* large list is created and           immediately deleted. */        i = op->ob_size;        while (--i >= 0) {            Py_XDECREF(op->ob_item[i]);        }        PyMem_FREE(op->ob_item);    }    if (num_free_lists < MAXFREELISTS && PyList_CheckExact(op))        free_lists[num_free_lists++] = op;    else        op->ob_type->tp_free((PyObject *)op);    Py_TRASHCAN_SAFE_END(op)}

在销毁PyListObject时,先减少list中的每个元素的引用计数,然后判断free_lists是否满了,没满就加上要删除的PyListObject,而下次创建PyListObject时,会优先从free_lists获取内存。而删除对象原来拥有的PyObject*列表,会因引用计数减少各归各处。

 


 

4、Hack PyListObject

可在list_print中添加:

printf("\nallocated=%d, ob_size=%d\n", op->allocated, op->ob_size);

观察PyListObject对内存的管理。

也可打印num_free_lists观察增删元素时对num_free_lists影响。

 

转载于:https://www.cnblogs.com/GO-NO-1/p/6516017.html

你可能感兴趣的文章
Maven学习总结(七)——eclipse中使用Maven创建Web项目
查看>>
Node.js中实现文件的循环写入
查看>>
大型网站技术架构(七)网站的可扩展性架构
查看>>
处理 ps cs3 打开提示 产品已停止 故障
查看>>
2015年终总结
查看>>
RSync实现文件备份同步
查看>>
linux系统定时任务
查看>>
js数组的操作 和 数组对象的3个属性
查看>>
POJ 1603: Risk
查看>>
我的友情链接
查看>>
QT5: 事件过滤.
查看>>
python学习1-Python内置下载服务器
查看>>
驾驭你的“职场布朗运动”
查看>>
我的友情链接
查看>>
Ubuntu14.04 系统下Django配置使用Postgresql数据库配置
查看>>
IDEA搭建Spring boot问题集
查看>>
String Stringbuffer和StringBuilder的区别
查看>>
游戏中,显示FPS
查看>>
Solaris crontab
查看>>
linux企业常用服务---squit传统代理
查看>>