Python 源码剖析(四)【LIST对象】

时间:2022-09-03 10:35:57

四、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 <= )
op->ob_item = NULL;
else {
op->ob_item = (PyObject **) PyMem_MALLOC(nbytes);
memset(op->ob_item, , 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 = ; 

放元素到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 < || i >= ((PyListObject *)op) -> ob_size) {
Py_XDECREF(newitem);
PyErr_SetString(PyExc_IndexError,
"list assignment index out of range");
return -;
}
p = ((PyListObject *)op) -> ob_item + i;
olditem = *p;
*p = newitem;
Py_XDECREF(olditem);
return ;
}

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+) == -)
return -; if (where < ) {
where += n;
if (where < )
where = ;
}
if (where > n)
where = n;
items = self->ob_item;
for (i = n; --i >= where; )
items[i+] = items[i];
Py_INCREF(v);
items[where] = v;
return ;
}

看看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 >> )) {
assert(self->ob_item != NULL || newsize == );
self->ob_size = newsize;
return ;
} /* 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 >> ) + (newsize < ? : ) + newsize;
if (newsize == )
new_allocated = ;
items = self->ob_item;
if (new_allocated <= ((~(size_t)) / sizeof(PyObject *)))
PyMem_RESIZE(items, PyObject *, new_allocated);
else
items = NULL;
if (items == NULL) {
PyErr_NoMemory();
return -;
}
self->ob_item = items;
self->ob_size = newsize;
self->allocated = new_allocated;
return ;
}

插入的时候,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 < ) {
where += n;
if (where < )
where = ;
}
if (where > n)
where = n;
items = self->ob_item;
for (i = n; --i >= where; )
items[i+] = items[i];
Py_INCREF(v);
items[where] = v;
return ;
}

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 -;
} static PyObject* listappend(PyListObject *self, PyObject *v)
{
if (app1(self, v) == )
Py_RETURN_NONE;
return NULL;
} static int app1(PyListObject *self, PyObject *v)
{
int n = PyList_GET_SIZE(self);
......
if (list_resize(self, n+) == -)
return -; Py_INCREF(v);
PyList_SET_ITEM(self, n, v);
return ;
}

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

2.3、删除

PyListObject的删除remove:

[listobject.c] 

static PyObject * listremove(PyListObject *self, PyObject *v)
{
int i;
for (i = ; i < self->ob_size; i++) {
int cmp = PyObject_RichCompareBool(self->ob_item[i], v, Py_EQ);
if (cmp > ) {
if (list_ass_slice(self, i, i+,(PyObject *)NULL) == )
Py_RETURN_NONE;
return NULL;
}
else if (cmp < )
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[];
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 = ;
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 < ) { /* 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 - ; k >= ; --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 >= ) {

            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影响。

Python 源码剖析(四)【LIST对象】的更多相关文章

  1. 《Python 源码剖析》之对象

    py一切皆对象的实现 Python中对象分为两类: 定长(int等), 非定长(list/dict等) 所有对象都有一些相同的东西, 源码中定义为PyObject和PyVarObject, 两个定义都 ...

  2. Python 源码剖析(一)【python对象】

    处于研究python内存释放问题,在阅读部分python源码,顺便记录下所得.(基于<python源码剖析>(v2.4.1)与 python源码(v2.7.6)) 先列下总结:      ...

  3. Python源码剖析——01内建对象

    <Python源码剖析>笔记 第一章:对象初识 对象是Python中的核心概念,面向对象中的"类"和"对象"在Python中的概念都为对象,具体分为 ...

  4. Python开发【源码剖析】 List对象

    前言 本文探讨的Python版本为2.7.16,可从官网上下载,把压缩包Python-2.7.16.tgz解压到本地即可 需要基本C语言的知识(要看的懂) PyListObject对象 PyListO ...

  5. Python源码剖析——02虚拟机

    <Python源码剖析>笔记 第七章:编译结果 1.大概过程 运行一个Python程序会经历以下几个步骤: 由解释器对源文件(.py)进行编译,得到字节码(.pyc文件) 然后由虚拟机按照 ...

  6. python源码剖析学习记录-01

    学习<Python源码剖析-深度探索动态语言核心技术>教程         Python总体架构,运行流程   File Group: 1.Core Modules 内部模块,例如:imp ...

  7. Python源码剖析&vert;百度网盘免费下载&vert;Python新手入门&vert;Python新手学习资料

    百度网盘免费下载:Python源码剖析|新手免费领取下载 提取码:g78z 目录  · · · · · · 第0章 Python源码剖析——编译Python0.1 Python总体架构0.2 Pyth ...

  8. Python 源码剖析 目录

    Python 源码剖析 作者: 陈儒 阅读者:春生 版本:python2.5 版本 本博客园的博客记录我会适当改成Python3版本 阅读 Python 源码剖析 对读者知识储备 1.C语言基础知识, ...

  9. Django Rest Framework源码剖析&lpar;四&rpar;-----API版本

    一.简介 在我们给外部提供的API中,可会存在多个版本,不同的版本可能对应的功能不同,所以这时候版本使用就显得尤为重要,django rest framework也为我们提供了多种版本使用方法. 二. ...

  10. Python 源码剖析(六)【内存管理机制】

    六.内存管理机制 1.内存管理架构 2.小块空间的内存池 3.循环引用的垃圾收集 4.python中的垃圾收集 1.内存管理架构 Python内存管理机制有两套实现,由编译符号PYMALLOC_DEB ...

随机推荐

  1. java&period;util&period;NoSuchElementException&colon; Timeout waiting for idle object

    出现这个问题第一个想法就是连接池的参数设置问题,把最大连接数量设置大一些就行了,但是我就一个客服端访问服务器,连接池连接数量不可能会不够用.我的项目架构是spring mvc+hibernate,用s ...

  2. SDC&lpar;3&rpar;&ndash&semi;set&lowbar;multicycle&lowbar;path 最关键的一张图

    上图意思是,假如使用 –setup option,默认约束的是 latch clock:假如使用 –hold option,默认约束的是 launch clock.箭头表示不同组合下时钟沿的移动方向. ...

  3. MyBatis insert后返回自增字段的值

    如下情况适用支持自增的DB,如MySQL.其他情况参见:MyBatis魔法堂:Insert操作详解(返回主键.批量插入) 1.model public class UserInfo { private ...

  4. 【前端】向blog或网站中添加语法高亮显示代码方法总结

    向blog或网站中添加语法高亮显示的代码方法总结 文章目录 预备知识 目标 第一类方法:嵌入 第二类方法:外部引用 第三类方法:忽略HTML和PHP 最近在写代码时遇到一个问题,就是如何让代码像在ID ...

  5. A&plus;B problems

    这几道习题大概是ACM输入输出格式的普及 P1:---------------------------------------------------------------------------- ...

  6. PHP查询数据库较慢,nginx 超时 返回 504:Sorry&comma; the page you are looking for is currently unavailable&period;

    现象: PHP查询数据库较慢,大约 60s 后 nginx 返回 504:Sorry, the page you are looking for is currently unavailable. 检 ...

  7. ELK收集Nginx自定义日志格式输出

    1.ELK收集日志的有两种常用的方式: 1.1:不修改源日志格式,简单的说就是在logstash中转通过 grok方式进行过滤处理,将原始无规则的日志转换为规则日志(Logstash自定义日志格式) ...

  8. C&num;复习题&lpar;概念&rpar; --C&num;学习笔记

    第一章 1.公共语言架构(CLI)由哪几部分组成? (1)通用类型系统:定义了一套类型系统的框架,规定了数据类型的声明.使用和管理方法. (2)公共语言规范:一组语言规则的集合 (3)通用中间语言:一 ...

  9. curl发送xml &comma; xml和数组互转

    public function postXml($url, array $data) { // pack xml $xml = $this->arrayToXml($data); // curl ...

  10. 使用Axure RP原型设计实践08&comma;制作圆角文本框

    本篇体验做一个简单圆角文本框,做到3个效果: 1.初始状态,圆角文本框有淡淡的背景色,边框的颜色为浅灰色2.点击圆角文本框,让其获取焦点,边框变成蓝色,背景色变成白色3.圆角文本框失去焦点,边框变成红 ...