Python字典对象实现原理详解

  

字典类型是Python中最常用的数据类型之一,它是一个键值对的集合,字典通过键来索引,关联到相对的值,理论上它的查询复杂度是O (1):

        在在在d={“a”: 1、“b”: 2}   在在在d ' c '=3   在在在d   {“a”: 1、“b”: 2,“c”: 3}      

在字符串的实现原理文章中,曾经出现过字典对象用于实习生操作,那么字典的内部结构是怎样的呢? PyDictObject对象就是dict的内部实现。

  


  

  

哈希表(也叫散列表),根据关键值对(键值)而直接进行访问的数据结构,它通过把键和值映射到表中一个位置来访问记录,这种查询速度非常快,更新也快。而这个映射函数叫做哈希函数,存放值的数组叫做哈希表,哈希函数的实现方式决定了哈希表的搜索效率。具体操作过程是:

  

1。数据添加:把键通过哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将价值存储在以该数字为下标的数组空间里。
  

  

2。数据查询:再次使用哈希函数将钥匙转换为对应的数组下标,并定位到数组的位置获取价值。
  

  

但是,对关键进行散列的时候,不同的关键可能哈希出来的结果是一样的,尤其是数据量增多的时候,这个问题叫做哈希冲突。如果解决这种冲突情况呢?通常的做法有两种,一种是链接法,另一种是开放寻址法,Python选择后者。

  

  

开放寻址法中,所有的元素都存放在散列表里,当产生哈希冲突时,通过一个探测函数计算出下一个候选位置,如果下一个获选位置还是有冲突,那么不断通过探测函数往下找,直到找个一个空槽来存放待插入元素。

  


  

  

字典中的一个键-值键值对元素称为条目(也叫做槽),对应到Python内部是PyDictEntry, PyDictObject就是PyDictEntry的集合.PyDictEntry的定义是:

        typedef struct {/* me_key缓存的散列码。注意,哈希码C多头。   *我们必须使用Py_ssize_t而不是因为dict_popitem()的滥用   * me_hash举行搜索的手指。   */Py_ssize_t me_hash;   PyObject * me_key;   PyObject * me_value;   }PyDictEntry;      

me_hash用于缓存me_key的哈希值,防止每次查询时都要计算哈希值,输入有三种状态。

  

1。未使用:me_key==me_value=https://www.yisu.com/zixun/=空

  

未使用的是条目的初始状态,键和值都为NULL。插入元素时,未使用的状态转换成活跃状态。这是me_key为零的唯一情况。

  

2。活动:me_key !=NULL和me_key !=假且me_value !=NULL

  

插入元素后,入口就成了活跃的状态,这是me_value唯一不为零的情况,删除元素时活跃状态刻转换成假状态。

  

3。假:me_key==假且me_value=https://www.yisu.com/zixun/=空

  

此处的虚拟对象实际上一个PyStringObject对象,仅作为指示标志.Dummy状态的元素可以在插入元素的时候将它变成活跃的状态,但它不可能再变成未使用的状态。

  

为什么条目有虚状态呢?这是因为采用开放寻址法中,遇到哈希冲突时会找到下一个合适的位置,例如某元素经过哈希计算应该插入到一处,但是此时一处有元素的,通过探测函数计算得到下一个位置B,仍然有元素,直到找到位置C为止,此时ABC构成了探测链,查找元素时如果散列值相同,那么也是顺着这条探测链不断往后找,当删除探测链中的某个元素时,比如B,如果直接把B从哈希表中移除,即变成未使用的状态,那么C就不可能再找到了,因为交流之间出现了断裂的现象,正是如此才出现了第三种状态——假,假是一种类似的伪删除方式,保证探测链的连续性。
  

  

 Python字典对象实现原理详解

  


  

  

PyDictObject就是PyDictEntry对象的集合,PyDictObject的结构是:

        typedef struct _dictobject PyDictObject;   struct _dictobject {   PyObject_HEAD   Py_ssize_t ma_fill;+ #假*//* #活跃   Py_ssize_t ma_used;*//* #活跃/*表包含ma_mask + 1插槽,这是2的幂。   *我们存储大小的面具而不是因为面具   *经常需要的。   */Py_ssize_t ma_mask;/* ma_table指向ma_smalltable对于小表、其他   *额外分配的内存。ma_table从来不是零!这条规则   *可以节省重复运行时在主力null-tests getitem和   * setitem调用。   */PyDictEntry * ma_table;   PyDictEntry * (* ma_lookup) (PyDictObject *议员PyObject *键,长散列);   PyDictEntry ma_smalltable [PyDict_MINSIZE];   

Python字典对象实现原理详解