《python源代码剖析》笔记 python中的Dict对象
本文为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对象的更多相关文章
- Python源代码剖析笔记3-Python运行原理初探
Python源代码剖析笔记3-Python执行原理初探 本文简书地址:http://www.jianshu.com/p/03af86845c95 之前写了几篇源代码剖析笔记,然而慢慢觉得没有从一个宏观 ...
- 《python源代码剖析》笔记 python中的List对象
本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.PyListObject对象 --> 变长可变对象,可看作vector<Py ...
- 《python源代码剖析》笔记 Python的编译结果
本文为senlie原创.转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.python的运行过程 1)对python源码进行编译.产生字节码 2)将编译结果交给p ...
- 《python源代码剖析》笔记 Python虚拟机框架
本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1. Python虚拟机会从编译得到的PyCodeObject对象中依次读入每一条字节码指令 ...
- 《python源代码剖析》笔记 python环境初始化
版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/zhsenl/article/details/33747209 本文为senlie原创.转载请保留此地 ...
- 《python解释器源码剖析》第6章--python中的dict对象
6.0 序 元素和元素之间可能存在着某种关系,比如学生姓名和成绩.我希望能够通过学生的姓名找到这个学生的成绩,那么只需要将两者关联起来即可.字典正是这么做的,字典中的每个元素就是一个key:value ...
- Python 源码剖析(五)【DICT对象】
五.DICT对象 1.散列表概述 2.PyDictObject 3.PyDictObject的创建与维护 4.PyDictObject 对象缓冲池 5.Hack PyDictObject 这篇篇幅较长 ...
- Redis源代码剖析和凝视(八)--- 对象系统(redisObject)
Redis 对象系统 1. 介绍 redis中基于双端链表.简单动态字符串(sds).字典.跳跃表.整数集合.压缩列表.高速列表等等数据结构实现了一个对象系统,而且实现了5种不同的对象,每种对象都使用 ...
- 《python源代码剖析》笔记 python虚拟机中的函数机制
本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.Python虚拟机在运行函数调用时会动态地创建新的 PyFrameObject对象, 这 ...
随机推荐
- pen: Local Testing
[踩点] * OLEViewer:查看 ActiveX 组件信息 [Fuzz] * Tools in This Article * COMRaider:ActiveX/ocx [utils] * Fi ...
- spring aop例子
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAATcAAAFWCAIAAACD6E2aAAAgAElEQVR4nO2df1gTV77/55/93z/2ee
- 配置Windows Server 2008 允许多用户远程桌面连接
开启远程桌面后,远程访问windows server 2008服务器时,默认只支持一个用户名同时只能创建一个远程连接,新建连接登录后会将前一个就踢掉,有没有办法像windows server 2005 ...
- SQL函数:用于将小写的数值翻译成大写的字符串
--功能: 用于将小写的数值翻译成大写的字符串(支持到分,即小数点后两位) --入口参数:@decNum------数字型变量 --返回:字符串 --举例:select db ...
- PHP Cookies
PHP Cookies cookie 常用于识别用户. Cookie 是什么? cookie 常用于识别用户.cookie 是一种服务器留在用户计算机上的小文件.每当同一台计算机通过浏览器请求页面时, ...
- 告诉你GetDC()没有释放造成的后果
最近做的项目中需要显示视频监控窗口,从采集卡中读到图像的数据,需要实时显示出来,而且速度比较快. 由于比较简单,就直接使用了GDI画图,以前复杂的都用openGL啥的工具了,这次这个简单,就直接用GD ...
- 利用谷歌 kaptcha 进行验证码生成
package main.com.smart.controller; import com.google.code.kaptcha.Producer; import main.com.smart.ut ...
- /etc/host 配置主机名字
每个机子中的hosts文件都应有下面域IP对应的文件
- Oracle数据库之PL/SQL程序设计简介
PL/SQL程序设计简介 一.什么是PL/SQL? PL/SQL是 Procedure Language & Structured Query Language 的缩写. ORACLE的SQL ...
- 【Jquery EasyUI + Servlet】DataGrid,url请求带中文出现乱码的解决方案
demo.jsp: <% String name = "乱码"; %> $(function(){ $('#dg').datagrid({ url: 'DemoServ ...