本文为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. pen: Local Testing

    [踩点] * OLEViewer:查看 ActiveX 组件信息 [Fuzz] * Tools in This Article * COMRaider:ActiveX/ocx [utils] * Fi ...

  2. spring aop例子

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAATcAAAFWCAIAAACD6E2aAAAgAElEQVR4nO2df1gTV77/55/93z/2ee

  3. 配置Windows Server 2008 允许多用户远程桌面连接

    开启远程桌面后,远程访问windows server 2008服务器时,默认只支持一个用户名同时只能创建一个远程连接,新建连接登录后会将前一个就踢掉,有没有办法像windows server 2005 ...

  4. SQL函数:用于将小写的数值翻译成大写的字符串

    --功能:  用于将小写的数值翻译成大写的字符串(支持到分,即小数点后两位)       --入口参数:@decNum------数字型变量    --返回:字符串    --举例:select db ...

  5. PHP Cookies

    PHP Cookies cookie 常用于识别用户. Cookie 是什么? cookie 常用于识别用户.cookie 是一种服务器留在用户计算机上的小文件.每当同一台计算机通过浏览器请求页面时, ...

  6. 告诉你GetDC()没有释放造成的后果

    最近做的项目中需要显示视频监控窗口,从采集卡中读到图像的数据,需要实时显示出来,而且速度比较快. 由于比较简单,就直接使用了GDI画图,以前复杂的都用openGL啥的工具了,这次这个简单,就直接用GD ...

  7. 利用谷歌 kaptcha 进行验证码生成

    package main.com.smart.controller; import com.google.code.kaptcha.Producer; import main.com.smart.ut ...

  8. /etc/host 配置主机名字

    每个机子中的hosts文件都应有下面域IP对应的文件

  9. Oracle数据库之PL/SQL程序设计简介

    PL/SQL程序设计简介 一.什么是PL/SQL? PL/SQL是 Procedure Language & Structured Query Language 的缩写. ORACLE的SQL ...

  10. 【Jquery EasyUI + Servlet】DataGrid,url请求带中文出现乱码的解决方案

    demo.jsp: <% String name = "乱码"; %> $(function(){ $('#dg').datagrid({ url: 'DemoServ ...