Python字典顺序存储原理解析

简介: 在刷题的时候看到很多时候题目要用到OrderedDict,不是很理解这样做的目的,看到解析说是要按照插入的顺序存储和取出。当时就很疑惑,亲自试验了默认的dict也能够实现顺序存储和取出。

Dictionary vs OrderedDict


在3.6版本之前,Python Dict底层在初始创建的时候采用的是indice和存储合并在一个二维数组当中。Dictionary采用哈希表原理,key作为取值对象,进行hash(key)操作,得到哈希值,然后用值进行 % 字典容量得到要插入的位置。

my_dict['age'] = 26
my_dict['salary'] = 999999
## Dictionary结构
[[-4234469173262486640, 指向salary的指针, 指向999999的指针],
[1545085610920597121, 执行age的指针, 指向26的指针],
[---, ---, ---],
[---, ---, ---],
[---, ---, ---],
[1278649844881305901, 指向name的指针, 指向kingname的指针],
[---, ---, ---],
[---, ---, ---]]

取值 和存放都是进行hash然后取模,直接访问这个二位数组。当你要循环遍历字典的Key的时候,Python底层会遍历这个二维数组,如果当前行有数据,那么就返回Key指针对应的内存里面的值。如果当前行没有数据,那么就跳过。所以总是会遍历整个二位数组的每一行。


每一行有三列,每一列占用8byte的内存空间,所以每一行会占用24byte的内存空间。


由于Hash值取余数以后,余数可大可小,所以字典的Key并不是按照插入的顺序存放的


注意,这里我省略了与本文没有太大关系的两个点:


  1. 1.开放寻址,当两个不同的Key,经过Hash以后,再对8取余数,可能余数会相同。此时Python为了不覆盖之前已有的值,就会使用开放寻址技术重新寻找一个新的位置存放这个新的键值对。

  2. 2.当字典的键值对数量超过当前数组长度的2/3时,数组会进行扩容,8行变成16行,16行变成32行。长度变了以后,原来的余数位置也会发生变化,此时就需要移动原来位置的数据,导致插入效率变低。


在版本3.6之后,字典的底层数据结构发生了变化,现在当你初始化一个空的字典以后,它在底层是这样的:

my_dict['address'] = 'xxx'
my_dict['salary'] = 999999
## 此时的内存示意图
indices = [1, 0, None, None, None, None, 2, None]
entries = [[-5954193068542476671, 指向name的指针, 执行kingname的指针],
          [9043074951938101872, 指向address的指针,指向xxx的指针],
          [7324055671294268046, 指向salary的指针, 指向999999的指针]
         ]

实际数据存储和索引进行分开存放,indices是数据存放在二维数组的位置,其他内容保持不变。这样就保证了Dictionary在添加新的键值对的时候是按照顺序进行依次存放的。当去读取dict内容的时候

>>> hash('salary')
7324055671294268046
>>> hash('salary') % 8
6

那么我就去读indices下标为6的这个值。这个值为2.


然后再去读entries里面,下标为2的这一行的数据,也就是salary对应的数据了。


新的这种方式,当我要插入新的数据的时候,始终只是往entries的后面添加数据,这样就能保证插入的顺序。当我们要遍历字典的Keys和Values的时候,直接遍历entries即可,里面每一行都是有用的数据,不存在跳过的情况,减少了遍历的个数。


老的方式,当二维数组有8行的时候,即使有效数据只有3行,但它占用的内存空间还是 8 * 24 = 192 byte。但使用新的方式,如果只有三行有效数据,那么entries也就只有3行,占用的空间为3 * 24 =72 byte,而indices由于只是一个一维的数组,只占用8 byte,所以一共占用 80 byte。内存占用只有原来的41%。

相关文章
|
1天前
|
XML JavaScript 数据格式
Beautiful Soup 库的工作原理基于解析器和 DOM(文档对象模型)树的概念
Beautiful Soup 使用解析器(如 html.parser, lxml, html5lib)解析HTML/XML文档,构建DOM树。它提供方法查询和操作DOM,如find(), find_all()查找元素,get_text(), get()提取信息。还能修改DOM,添加、修改或删除元素,并通过prettify()输出格式化字符串。它是处理网页数据的利器,尤其在处理不规则结构时。
6 2
|
3天前
|
开发者 Python
【Python 基础】递推式构造字典(dictionary comprehension)
【5月更文挑战第8天】【Python 基础】递推式构造字典(dictionary comprehension)
|
3天前
|
XML 存储 数据格式
python path解析基础
python path解析基础
10 0
|
3天前
|
数据采集 Python
Python HTML解析详解
Python HTML解析详解
6 0
|
3天前
|
机器学习/深度学习 人工智能 数据可视化
号称能打败MLP的KAN到底行不行?数学核心原理全面解析
Kolmogorov-Arnold Networks (KANs) 是一种新型神经网络架构,挑战了多层感知器(mlp)的基础,通过在权重而非节点上使用可学习的激活函数(如b样条),提高了准确性和可解释性。KANs利用Kolmogorov-Arnold表示定理,将复杂函数分解为简单函数的组合,简化了神经网络的近似过程。与mlp相比,KAN在参数量较少的情况下能达到类似或更好的性能,并能直观地可视化,增强了模型的可解释性。尽管仍需更多研究验证其优势,KAN为深度学习领域带来了新的思路。
47 5
|
3天前
|
敏捷开发 测试技术 持续交付
极限编程(XP)原理与技巧:深入解析与实践
【5月更文挑战第8天】极限编程(XP)是一种敏捷开发方法,注重快速反馈、迭代开发和简单设计,以提高软件质量和项目灵活性。关键原则包括客户合作、集体代码所有权、持续集成等。实践中,使用故事卡片描述需求,遵循编程约定,实行TDD,持续重构,结对编程,并定期举行迭代会议。通过理解和应用XP,团队能提升效率,应对变化。
|
5天前
|
存储 Python
【Python 基础】解释reduce函数的工作原理
【5月更文挑战第6天】【Python 基础】解释reduce函数的工作原理
|
5天前
|
Python
【Python 基础】解释map函数的工作原理
【5月更文挑战第6天】【Python 基础】解释map函数的工作原理
|
5天前
|
缓存 自然语言处理 JavaScript
万字长文深度解析JDK序列化原理及Fury高度兼容的极致性能实现
Fury是一个基于JIT动态编译的高性能多语言原生序列化框架,支持Java/Python/Golang/C++/JavaScript等语言,提供全自动的对象多语言/跨语言序列化能力,以及相比于别的框架最高20~200倍的性能。
|
5天前
|
JSON 安全 前端开发
解析FormData格式数据:Python实践指南
解析FormData格式数据:Python实践指南
13 1

推荐镜像

更多