zoukankan      html  css  js  c++  java
  • 《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对象时,假设在缓冲池中有能够使用的对象,则直接从缓冲池中取出使用,而不须要又一次创建




  • 相关阅读:
    jQuery属性遍历、HTML操作
    jQuery效果函数
    【Solr初探】Solr安装,启动,查询,索引
    【mac osx安装opencv,python总结】
    反射给对象赋值——类型转换
    js 模拟a标签打开新网页
    MVC post 方法导出word文档
    Jquery ajax json 值回传不了
    商城
    批处理的赋值 调用 参数
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/3808783.html
Copyright © 2011-2022 走看看