热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Python字典实现源码解读

pythondict源码解读



python dict 源码解读

  • python dict的基本介绍
  • Hash Table 概念
  • dict实现的三个核心结构体
  • 解读dict的底层几个C API源码

python dict的基本介绍

一般在编程语言里,需要一种数据结构,来映射一些关系,比如人的名字、年龄、性别等等

如图所示


























keyvalue
name张三
age18
sex

当我们输入 'name’时,希望能得到 ‘张三’

在Python里dict字典就是实现这个功能的一个内置数据类型

上表中的每一对key-value都可以称为一个条目(Entry),根据key就能找到value,是不是类似一个字典。

python中的dict语法如下

>>>student = dict(name="张三", age=18, sex="男")
>>>student
{"name":"张三", "age":18, "sex":"男"}
>>>student["name"]
张三
>>>

dict[key]–>value

Hash Table 概念

Python的字典是采用了散列表,或者叫哈希表,因为理论上,在最优的情况下,散列表能提供O(1)的复杂度的搜索效率。python的实现中本身大量使用了字典,比如在正常情况下,每个对象都有一个__dict__属性,再比如函数的关键字参数**kwargs等等,都依赖于python的字典,所以搜索效率是python实现字典的第一首要目标。

那么什么是Hash Table呢?

散列表的基本思想就是,通过一个函数,将key映射称为一个索引,去访问某一片连续内存区域,而这个索引所在的内存就存放了value的地方。

所以说,散列表是普通数组的推广应用。核心就是:根据关键字计算出key在表中的地址。

看图:

Python字典实现--源码解读 - 文章图片

dict实现的三个核心结构体

因为python3.6以后,字典变化较大,最大的变化就是dict变得有序了,并且效率提高了20%~30%,特别内存利用率更高了。

接下来,让我们看看C层面的关于字典实现的三个结构体

第一个核心结构体PyDictKeyEntry

位置:cpython/Objects/dict-common.h

typedef struct {
/* Cached hash code of me_key. */
Py_hash_t me_hash;
PyObject *me_key;
PyObject *me_value; /* This field is only meaningful for combined tables */
} PyDictKeyEntry;

从名字可以看出来,这是dict存储每一对key-value的结构体,也就是之前所说的Entry条目。

dict中的每一对key-value对应一个PyDictKeyEntry类型的对象。

1.me_hash:存储了key的哈希值,专门用一个成员记录key的散列值,可以避免每次查询时都要去重新计算下。

2.me_value:可以看到,在PyDictKeyEntry中value是一个PyObject *,这也是Python中的dict什么都能装的下的原因,因为在Python里,无论什么东西归根结点都是一个PyObject对象。

3.me_key:在一个PyDictObject对象变化的过程中,其中的entry会在不同的状态中转换。

PyDictObject中的entry可以在4种状态中转换:Unused(态)、Active(态)、Dummy(态)和Pending(态)

Unused:当一个entry处于Unused态时,entry的me_key和me_value都为NULL,这种情况下,表示这个entry并没有存储一个(key,value),并且之前也没有存储过它们,每一个entry在初始化的时候都会处于这种状态。而且只有在Unused态下,一个entry的me_key才会为NULL

Active:当一个entry存储了一个(key,value)时,entry便转换到了Active态,在这种状态下,me_key和me_value都不能为NULL,更准确的讲me_key不能为dummy对象

第二个核心结构体PyDictKeysObject

位置:cpython/Objects/dict-common.h

typedef struct _dictkeysobject PyDictKeysObject;
/* See dictobject.c for actual layout of DictKeysObject */
struct _dictkeysobject {
Py_ssize_t dk_refcnt;
/* Size of the hash table (dk_indices). It must be a power of 2. */
Py_ssize_t dk_size;
/* Function to lookup in the hash table (dk_indices):
- lookdict(): general-purpose, and may return DKIX_ERROR if (and
only if) a comparison raises an exception.
- lookdict_unicode(): specialized to Unicode string keys, comparison of
which can never raise an exception; that function can never return
DKIX_ERROR.
- lookdict_unicode_nodummy(): similar to lookdict_unicode() but further
specialized for Unicode string keys that cannot be the value.
- lookdict_split(): Version of lookdict() for split tables. */
dict_lookup_func dk_lookup;
/* Number of usable entries in dk_entries. */
Py_ssize_t dk_usable;
/* Number of used entries in dk_entries. */
Py_ssize_t dk_nentries;
/* Actual hash table of dk_size entries. It holds indices in dk_entries,
or DKIX_EMPTY(-1) or DKIX_DUMMY(-2).
Indices must be: 0 <= indice The size in bytes of an indice depends on dk_size:
- 1 byte if dk_size <= 0xff (char*)
- 2 bytes if dk_size <= 0xffff (int16_t*)
- 4 bytes if dk_size <= 0xffffffff (int32_t*)
- 8 bytes otherwise (int64_t*)
Dynamically sized, SIZEOF_VOID_P is minimum. */
char dk_indices[]; /* char is required to avoid strict aliasing. */
/* "PyDictKeyEntry dk_entries[dk_usable];" array follows:
see the DK_ENTRIES() macro */
};

第三个核心结构体PyDictObject

位置:cpython/Include/cpython/dictobject.h

typedef struct _dictkeysobject PyDictKeysObject;
/* The ma_values pointer is NULL for a combined table
* or points to an array of PyObject* for a split table
*/
typedef struct {
PyObject_HEAD
/* Number of items in the dictionary */
Py_ssize_t ma_used;
/* Dictionary version: globally unique, value change each time
the dictionary is modified */
uint64_t ma_version_tag;
PyDictKeysObject *ma_keys;
/* If ma_values is NULL, the table is "combined": keys and values
are stored in ma_keys.
If ma_values is not NULL, the table is splitted:
keys are stored in ma_keys and values are stored in ma_values */
PyObject **ma_values;
} PyDictObject;

1.PyObject_HEAD:就不用多说了,这是所有Python对象共有的,包含了两个成员,一个是引用计数,一个是指向对象所属的类型的指针。

2.ma_used:表示字典中条目个数,当我们使用内置函数len()去获取字典的长度时,底层直接返回就是这个成员的值

3.ma_version_tag:字典版本号,全局唯一,每次字典更改了,这个值也要改变

4.ma_keys:是一个指针,指向另一个核心结构体PyDictKeysObject,它是实现字典的关键所在。

5.ma_values:是一个指向指针的指针,当它为NULL时,散列表是组合的(combined),key和value存储在ma_keys里;当它不为NULL时,散列表是分离的(splited),key存储在ma_keys里,而value存储在ma_values里

解读dict的底层几个C API源码

推荐阅读
  • Todayatworksomeonetriedtoconvincemethat:今天在工作中有人试图说服我:{$obj->getTableInfo()}isfine ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • DSP中cmd文件的命令文件组成及其作用
    本文介绍了DSP中cmd文件的命令文件的组成和作用,包括链接器配置文件的存放链接器配置信息、命令文件的组成、MEMORY和SECTIONS两个伪指令的使用、CMD分配ROM和RAM空间的目的以及MEMORY指定芯片的ROM和RAM大小和划分区间的方法。同时强调了根据不同芯片进行修改的必要性,以适应不同芯片的存储用户程序的需求。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
  • 本文介绍了在Mac上搭建php环境后无法使用localhost连接mysql的问题,并通过将localhost替换为127.0.0.1或本机IP解决了该问题。文章解释了localhost和127.0.0.1的区别,指出了使用socket方式连接导致连接失败的原因。此外,还提供了相关链接供读者深入了解。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 超级简单加解密工具的方案和功能
    本文介绍了一个超级简单的加解密工具的方案和功能。该工具可以读取文件头,并根据特定长度进行加密,加密后将加密部分写入源文件。同时,该工具也支持解密操作。加密和解密过程是可逆的。本文还提到了一些相关的功能和使用方法,并给出了Python代码示例。 ... [详细]
  • Python中sys模块的功能及用法详解
    本文详细介绍了Python中sys模块的功能及用法,包括对解释器参数和功能的访问、命令行参数列表、字节顺序指示符、编译模块名称等。同时还介绍了sys模块中的新功能和call_tracing函数的用法。推荐学习《Python教程》以深入了解。 ... [详细]
  • Spring框架《一》简介
    Spring框架《一》1.Spring概述1.1简介1.2Spring模板二、IOC容器和Bean1.IOC和DI简介2.三种通过类型获取bean3.给bean的属性赋值3.1依赖 ... [详细]
author-avatar
对不起太依赖你
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有