本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie

1.PyDictObject对象 -->  C++ STL中的map是基于RB-tree的,搜索时间复杂度是O(logN)

PyDictObject採用了hash表,时间复杂度是O(1)

typedef struct{
Py_ssize_t me_hash; //me_key的hash值,避免每次查询都要又一次计算一遍hash值
PyObject *me_key;
PyObject *me_value;
}PyDictEntry;

将(key,value)对称为entry,它能够在3种状态间转换:

Unused态 --> me_key和 me_value都为NULL

Active态 --> me_key和 me_value都不为NULL

Dummy态  --> me_key为dummy, me_value为NULL

typedef struct _dictobject PyDictObject;
struct _dictobject{
PyObject_HEAD
Py_ssize_t ma_fill; //元素个数: Active + Dummy
Py_ssize_t ma_used; //元素个数: Active
Py_ssize_t ma_mask; //记录了entry的数量 PyDictEntry *ma_table;
PyDictEntry *(*ma_lookup)(PyDictObject *mp, PyObject *key, long hash);
PyDictEntry ma_smalltable[PyDict_MINSIZE];
}

PyDict_MINSIZE默认设定为8

当 PyDictObject对象的entry数量少于8个, ma_table将指向 ma_smalltable

当 PyDictObject对象的entry数量大于8个, ma_table将指向额外申请的内存空间

Q:这个时候 ma_smalltable中的对象怎么办?

A:ma_smalltable里的对象全都拷贝到新的table里





PyDict_Type --> PyDictObject的类型对象

PyTypeObject PyDict_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"dict",
sizeof(PyDictObject),
0,
(destructor)dict_dealloc, /* tp_dealloc */
(printfunc)dict_print, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
(cmpfunc)dict_compare, /* tp_compare */
(reprfunc)dict_repr, /* tp_repr */
0, /* tp_as_number */
&dict_as_sequence, /* tp_as_sequence */
&dict_as_mapping, /* tp_as_mapping */
(hashfunc)PyObject_HashNotImplemented, /* tp_hash */
//...
};

2.PyDictObject的创建

一个途径:

PyDict_New

PyObject* PyDict_New(void)
{
register dictobject *mp;
//[1]:自己主动创建 dummy对象
//防止探測序列中断
if (dummy == NULL) { /* Auto-initialize dummy */
dummy = PyString_FromString("<dummy key>");
}
if (num_free_dicts)
{
…… //[2]:使用缓冲池
}
else
{
//[3]:创建 PyDictObject对象
mp = PyObject_GC_New(dictobject, &PyDict_Type);
EMPTY_TO_MINSIZE(mp);
}
mp->ma_lookup = lookdict_string;
return (PyObject *)mp;
}
//EMPTY_TO_MINSIZE --> ma_size, ma_fill = 0
//INIT_NONZERO_DICT_SLOT --> 将 ma_table指向 ma_smalltable

元素搜索

static PyDictEntry *
lookdict(PyDictObject *mp, PyObject *key, register long hash)
{
register size_t i;
register size_t perturb;
register PyDictEntry *freeslot; //freeslot用来指向探測序列中第一个处于Dummy态的entry
register size_t mask = (size_t)mp->ma_mask;
PyDictEntry *ep0 = mp->ma_table;
register PyDictEntry *ep;
register int cmp;
PyObject *startkey; //[1]:hash,定位冲突探測链的第一个entry
i = (size_t)hash & mask; //将哈希值映射到哈希表大小范围内
ep = &ep0[i]; //[2]:
//1. entry处于Unused态
//2. entry中的 key与待搜索的 key匹配
if (ep->me_key == NULL || ep->me_key == key) //**引用同样检查
return ep; //[3]:第一个entry处于 Dummy态,设置freeslot
if (ep->me_key == dummy)
freeslot = ep;
else {
//检查Active态entry
if (ep->me_hash == hash) {
startkey = ep->me_key;
cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);//**值同样检查
if (cmp < 0)
return NULL;
}
freeslot = NULL;
} for (perturb = hash; ; perturb >>= PERTURB_SHIFT) {
//[5]:寻找探測链上下一个entry
i = (i << 2) + i + perturb + 1;//?
ep = &ep0[i & mask];
//[6]:到达 Unused态 entry,搜索失败
if (ep->me_key == NULL)
//假设 freeslot不为空,返回freeslot所指的entry
//假设 freeslot为空,返回该 Unused态的 entry
return freeslot == NULL ? ep : freeslot;
if (ep->me_key == key) //“引用同样”规则检查
return ep;
if (ep->me_hash == hash && ep->me_key != dummy) { // "值同样"规则检查
startkey = ep->me_key;
cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);
if (cmp > 0)
return ep;
}
else {
return lookdict(mp, key, hash);
}
}
else if (ep->me_key == dummy && freeslot == NULL)
freeslot = ep;
}
}
//失败或成功返回entry,失败的话,entry的me_value为NULL

插入与删除

insertdict

1.搜索成功,返回处于Active态的 entry,直接替换 me_value

2.搜索失败,返回 Unused态或 Dummy态的entry,完整设置 me_key,me_hash,me_value

在调用insertdict之前会调用PyDict_SetItem,是由 PyDict_SetItem调用 insertdict的

1.计算hash值

2.插入(key, value)元素对 //在这里调用insertdict

3.必要时调整dict的内存空间 //当搜索失败且装载率大于或等于2/3时就调整dict的内存空间

调用 dictresize改变dict的table大小

1.确定新的table的大小

2.假设新的table大小为8,就能够直接使用ma_smalltable

3.否则,须要在堆上申请空间

4.设置新的table

5.处理旧table中的entry:

  Active态entry,搬移到新的table中

  Dummy态的entry,调整 key的引用计数,丢弃该entry

6.必要时释放旧table所维护的内存空间

PyDict_DelItem

1.获得hash值

2.搜索entry

3.删除 entry所维护的元素,将 entry的状态转为 dummy态



3.PyDictObject对象缓冲池

与PyListObject中使用的缓冲池一样,最初什么都没有,当第一个PyDictObject当销毁时,

这个缓冲池才開始接纳被缓冲的PyDictObject对象

static void
dict_dealloc(register PyDictObject *mp)
{
register PyDictEntry *ep;
Py_ssize_t fill = mp->ma_fill;
//[1]:调整dict中对象的引用计数
for (ep = mp->ma_table; fill > 0; ep++) {
if (ep->me_key) {
--fill;
Py_DECREF(ep->me_key);
Py_XDECREF(ep->me_value);
}
}
//[2]:释放从系统堆中申请的内存空间
if (mp->ma_table != mp->ma_smalltable)
PyMem_DEL(mp->ma_table);
//[3]:将被销毁的PyDictObject对象放入缓冲池
if (numfree < PyDict_MAXFREELIST && Py_TYPE(mp) == &PyDict_Type)
free_list[numfree++] = mp;
else
Py_TYPE(mp)->tp_free((PyObject *)mp);
Py_TRASHCAN_SAFE_END(mp)
}

在创建新的 PyDictObject对象时,假设在缓冲池中有能够使用的对象,则直接从缓冲池中取出使用,而不须要又一次创建

《python源代码剖析》笔记 python中的Dict对象的更多相关文章

  1. Python源代码剖析笔记3-Python运行原理初探

    Python源代码剖析笔记3-Python执行原理初探 本文简书地址:http://www.jianshu.com/p/03af86845c95 之前写了几篇源代码剖析笔记,然而慢慢觉得没有从一个宏观 ...

  2. 《python源代码剖析》笔记 python中的List对象

    本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.PyListObject对象 --> 变长可变对象,可看作vector<Py ...

  3. 《python源代码剖析》笔记 Python的编译结果

    本文为senlie原创.转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.python的运行过程 1)对python源码进行编译.产生字节码 2)将编译结果交给p ...

  4. 《python源代码剖析》笔记 Python虚拟机框架

    本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1. Python虚拟机会从编译得到的PyCodeObject对象中依次读入每一条字节码指令 ...

  5. 《python源代码剖析》笔记 python环境初始化

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/zhsenl/article/details/33747209 本文为senlie原创.转载请保留此地 ...

  6. 《python解释器源码剖析》第6章--python中的dict对象

    6.0 序 元素和元素之间可能存在着某种关系,比如学生姓名和成绩.我希望能够通过学生的姓名找到这个学生的成绩,那么只需要将两者关联起来即可.字典正是这么做的,字典中的每个元素就是一个key:value ...

  7. Python 源码剖析(五)【DICT对象】

    五.DICT对象 1.散列表概述 2.PyDictObject 3.PyDictObject的创建与维护 4.PyDictObject 对象缓冲池 5.Hack PyDictObject 这篇篇幅较长 ...

  8. Redis源代码剖析和凝视(八)--- 对象系统(redisObject)

    Redis 对象系统 1. 介绍 redis中基于双端链表.简单动态字符串(sds).字典.跳跃表.整数集合.压缩列表.高速列表等等数据结构实现了一个对象系统,而且实现了5种不同的对象,每种对象都使用 ...

  9. 《python源代码剖析》笔记 python虚拟机中的函数机制

    本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.Python虚拟机在运行函数调用时会动态地创建新的 PyFrameObject对象, 这 ...

随机推荐

  1. webform 复杂点的服务器控件

    1  , dropdownlist:  下拉框 属性items  列表集合,  里面的每一个元素是一个 listitem . 联动的时候注意要 设置属性 .Autopostback 为ture: 注注 ...

  2. SQL语句添加删除修改字段及一些表与字段的基本操作

    用SQL语句添加删除修改字段 1.增加字段     alter table docdsp    add dspcode char(200)2.删除字段     ALTER TABLE table_NA ...

  3. 新闻源图片放到js里

    例子:http://www.s1979.com/jkys/20141209/2547965.html <script type="text/javascript" src=& ...

  4. z-index优先级总结

    因为显示器显示的图案是一个二维平面,拥有x轴和y轴来表示位置属性.为了表示三维立体的概念如显示元素的上下层的叠加顺序引入了z-index属性来表示z轴的区别,表示一个元素在叠加顺序上的上下立体关系. ...

  5. memcache锁,解决查询过多email查询为空的问题

    /* 设置memcache锁,解决查询过多email查询为空的问题 Begin */ $mmc = new Memcache; $mmc->connect('127.0.0.1', 11211) ...

  6. 马士兵SVN.

    下载 服务端:VisualSVN Server 和客户端:TortoiseSVN cmd,并cd 到 VisualSVN Server安装目录下的bin目录. 新建库: svnadmin create ...

  7. Delphi 做ActiveX的详细过程

    1.新建 如下图 点击OK 依然点击OK 出现了如上图的节面,就像窗体一样. 然后 你就想干什么干什么. 这个做好之后, 这个是我设计的窗体. 然后 就添加 外部可以调用的接口了. 如果你不想让外部调 ...

  8. 画板社交工具开发分享——HTML5 canvas控件、PHP、社交分享学习(一)

    不谈国内社交网站(人人.微博等)目前的盈利状况如何,facebook.twitter都已经取得了很大的成功.这一定程度上都得益于人们对社交的喜爱和投入. 目前的情况来看,也有很多人已经对直接的文字分享 ...

  9. JQUERY1.9学习笔记 之基本过滤器(五) 大于选择器

    大于选择器:jQuery( ":gt(index)" )jQuery( ":gt(-index)" ) 例:大于TD5 到TD8 用黄色背景,TD8用红色文字. ...

  10. 提高PHP性能的方法技巧

    1.用单引号代替双引号来包含字符串,这样做会更快一些.因为PHP会在双引号包围的字符串中搜寻变量,单引号则不会,注意:只有echo能这么做,它是一种可以把多个字符串当作参数的“函数”(译注:PHP手册 ...