专栏名称: 程序员大咖
为程序员提供最优质的博文、最精彩的讨论、最实用的开发资源;提供最新最全的编程学习资料:PHP、Objective-C、Java、Swift、C/C++函数库、.NET Framework类库、J2SE API等等。并不定期奉送各种福利。
目录
相关文章推荐
程序员的那些事  ·  突发!o3-mini ... ·  2 天前  
OSC开源社区  ·  继V3之后,沐曦GPU再完成DeepSeek ... ·  2 天前  
程序员小灰  ·  这款AI编程工具,将会取代Cursor! ·  2 天前  
程序员小灰  ·  这个春节,小灰一天都没休息 ·  4 天前  
51好读  ›  专栏  ›  程序员大咖

Python 源码阅读:dict

程序员大咖  · 公众号  · 程序员  · 2017-10-16 10:24

正文

点击上方“ 程序员大咖 ”,选择“置顶公众号”

关键时刻,第一时间送达!


源码位置 Include/dictobject.h | Objects/dictobject.c


PyDictObject的存储策略


1. 使用散列表进行存储

2. 使用开放定址法处理冲突

2.1 插入 , 发生冲突 , 通过二次探测算法 , 寻找下一个位置 , 直到找到可用位置 , 放入 ( 形成一条冲突探测链 )

2.2 查找 , 需要遍历冲突探测链

2.3 删除 , 如果对象在探测链上 , 不能直接删除 , 否则会破坏整个结构 ( 所以不是真的删 )


关于 hash表的 wiki


基本键值PyDictEntry


typedef struct {

Py_ssize_t me_hash ;

PyObject * me_key ;

PyObject * me_value ;

} PyDictEntry ;


说明


1. PyDictEntry 用于存储键值对信息

2. Py_ssize_t me _ hash

存储了 me _ key计算得到的 hash , 不重复计算


结构


PyDictEntry的三个状态(图片引自-Python源码剖析)


PyDictObject定义


定义


typedef struct _dictobject PyDictObject ;

struct _dictobject {

PyObject_HEAD

Py_ssize_t ma_fill ;

Py_ssize_t ma_used ;

Py_ssize_t ma_mask ;

PyDictEntry * ma_table ;

PyDictEntry * ( * ma_lookup )( PyDictObject * mp , PyObject * key , long hash );

PyDictEntry ma_smalltable [ PyDict_MINSIZE ];

};


说明


1. PyObject _ HEAD

反而声明为定长对象 , 因为 ob _ size在这里用不上 , 使用 ma _ fill和 ma _ used计数

2. Py_ssize_t ma_fill ;

Py_ssize_t ma_used ;

ma_fill = # Active + # Dummy

ma_used = # Active

3. Py_ssize_t ma_mask ;

散列表 entry 容量 = ma_mask + 1 , 初始值 ma_mask = PyDict_MINSIZE - 1 = 7

ma_mask + 1 = # Unused + # Active + # Dummy

4. PyDictEntry * ma_table ;

指向散列表内存 , 如果是小的 dict ( entry 数量


结构


结论


1. PyDictObject 在生命周期内 , 需要维护 ma_fill / ma_used / ma _ mask 三个计数值

2. 初始化创建是 ma_smalltable , 超过大小后 , 会使用外部分配的空间


构造过程


定义


PyObject *

PyDict_New ( void )

{

register PyDictObject * mp ;

// 初始化dummy

if ( dummy == NULL ) {

dummy = PyString_FromString ( "" );

if ( dummy == NULL )

return NULL ;

// 暂时忽略

#ifdef SHOW_CONVERSION_COUNTS

Py_AtExit ( show_counts );

#endif

#ifdef SHOW_ALLOC_COUNT

Py_AtExit ( show_alloc );

#endif

#ifdef SHOW_TRACK_COUNT

Py_AtExit ( show_track );

#endif

}

// 如果PyDictObject缓冲池可用

if ( numfree ) {

// 取缓冲池最后一个可用对象

mp = free_list [ -- numfree ];

assert ( mp != NULL );

assert ( Py_TYPE ( mp ) == & PyDict_Type );

_Py_NewReference (( PyObject * ) mp );

// 初始化

if ( mp -> ma_fill ) {

// 1. 清空 ma_smalltable

// 2. ma_used = ma_fill = 0

// 3. ma_table -> ma_smalltable

// 4. ma_mask = PyDict_MINSIZE - 1 = 7

EMPTY_TO_MINSIZE ( mp );

} else {

// 1. ma_table -> ma_smalltable

// 2. ma_mask = PyDict_MINSIZE - 1 = 7

INIT_NONZERO_DICT_SLOTS ( mp );

}

assert ( mp -> ma_used == 0 );

assert ( mp -> ma_table == mp -> ma_smalltable );

assert ( mp -> ma_mask == PyDict_MINSIZE - 1 );

#ifdef SHOW_ALLOC_COUNT

count_reuse ++ ;

#endif

} else {

// 创建一个

mp = PyObject_GC_New ( PyDictObject , & PyDict_Type );

if ( mp == NULL )

return NULL ;

// 初始化 1/2/3/4

EMPTY_TO_MINSIZE ( mp );

#ifdef SHOW_ALLOC_COUNT

count_alloc ++ ;

#endif

}

// 搜索方法, 关注这个

mp -> ma_lookup = lookdict_string ;

#ifdef SHOW_TRACK_COUNT

count_untracked ++ ;

#endif

#ifdef SHOW_CONVERSION_COUNTS

++ created ;

#endif

// 返回

return ( PyObject * ) mp ;

}


简化步骤


1. 如果 PyDictObject 对象缓冲池有 , 从里面直接取 , 初始化

2. 否则 , 创建一个 , 初始化

3. 关联搜索方法 lookdict _ string

4. 返回


结论


1. 关注对象缓冲池

2. 关注 lookdict_string


销毁过程


方法定义


static void

dict_dealloc (







请到「今天看啥」查看全文