一、数据类型划分之一
可分为:可变数据类型,不可变数据类型
不可变数据类型:元祖,布尔值(Bool),数值 int ,字符串 str 可哈希
可变数据类型: list,dict,set 不可哈希
集合 set:本身是可变数据类型,但集合里面的元素是不可变数据类型。
二、字典
字典是python中,唯一一个影射型数据类型。
dict 的 key 必须是不可变数据类型,可哈希。 不然会报错。
value:任意数据类型
dict 优点:key,可哈希,哈希表 内存中对应相应位置,二分法查找,查询速度快。
储存大量的关系型数据。
特点:字典是无序的。(3.5及3.5之前都是无序的) 打印看每次顺序是否一致验证。
Why:咱们目前已经学习到的容器型数据类型只有list,那么list够用?他有什么缺点呢?
1. 列表可以存储大量的数据类型,但是如果数据量大的话,他的查询速度比较慢。
2. 列表只能按照顺序存储,数据与数据之间关联性不强。
所以针对于上的缺点,说咱们需要引入另一种容器型的数据类型,解决上面的问题,这就需要dict字典。
what:
数据类型可以按照多种角度进行分类,就跟咱们人一样,人按照地域可以划分分为亚洲人,欧洲人,美洲人等,但是按照肤色又可以分为白种人,黄种人,黑种人,等等,数据类型可以按照不同的角度进行分类,先给大家按照可变与不可变的数据类型的分类:
不可变(可哈希)的数据类型:int,str,bool,tuple。
可变(不可哈希)的数据类型:list,dict,set。
字典是Python语言中的映射类型,他是以{}括起来,里面的内容是以键值对的形式储存的:
Key: 不可变(可哈希)的数据类型.并且键是唯一的,不重复的。
Value:任意数据(int,str,bool,tuple,list,dict,set),包括后面要学的实例对象等。
在Python3.5版本(包括此版本)之前,字典是无序的。
在Python3.6版本之后,字典会按照初建字典时的顺序排列(即第一次插入数据的顺序排序)。
当然,字典也有缺点:他的缺点就是内存消耗巨大。
字典查询之所以快的解释:(了解)
字典的查询速度非常快,简单解释一下原因:字典的键值对会存在一个散列表(稀疏数组)这样的空间中,每一个单位称作一个表元,表元里面记录着key:value,如果你想要找到这个key对应的值,先要对这个key进行hash获取一串数字咱们简称为门牌号(非内存地址),然后通过门牌号,确定表元,对比查询的key与被锁定的key是否相同,如果相同,将值返回,如果不同,报错。(这里只是简单的说一下过程,其实还是比较复杂的。),下面我已图形举例:
# 此段解释来源于《流畅的python》. 这一节笼统地描述了 Python 如何用散列表来实现 dict 类型,有些细节只是一笔带过,像 CPython 里的一些优化技巧 就没有提到。但是总体来说描述是准确的。 Python 源码 dictobject.c 模块(http://hg.python.org/cpython/file/tip/Objects/dictobject.c)里有丰富的注释,另外延伸阅 读中有对《代码之美》一书的引用。 为了简单起见,这里先集中讨论 dict 的内部结构,然后再延伸到集合上面。 散列表其实是一个稀疏数组(总是有空白元素的数组称为稀疏数组)。在一般的数据结构 教材中,散列表里的单元通常叫作表元(bucket)。在 dict 的散列表当中,每个键值对 都占用一个表元,每个表元都有两个部分,一个是对键的引用,另一个是对值的引用。因 为所有表元的大小一致,所以可以通过偏移量来读取某个表元。 因为 Python 会设法保证大概还有三分之一的表元是空的,所以在快要达到这个阈值的时 候,原有的散列表会被复制到一个更大的空间里面。 如果要把一个对象放入散列表,那么首先要计算这个元素键的散列值。Python 中可以用 hash() 方法来做这件事情,接下来会介绍这一点。 01. 散列值和相等性 内置的 hash() 方法可以用于所有的内置类型对象。如果是自定义对象调用 hash() 的话,实际上运行的是自定义的 __hash__。如果两个对象在比较的时候是相等的, 那它们的散列值必须相等,否则散列表就不能正常运行了。例如,如果 1 == 1.0 为 8 真,那么 hash(1) == hash(1.0) 也必须为真,但其实这两个数字(整型和浮点) 的内部结构是完全不一样的。 为了让散列值能够胜任散列表索引这一角色,它们必须在索引空间中尽量分散开来。 这意味着在最理想的状况下,越是相似但不相等的对象,它们散列值的差别应该越 大。示例 3-16 是一段代码输出,这段代码被用来比较散列值的二进制表达的不同。 注意其中 1 和 1.0 的散列值是相同的,而 1.0001、1.0002 和 1.0003 的散列值则非常不 同。 示例 3-16 在32 位的 Python 中,1、1.0001、1.0002 和 1.0003 这几个数的散列 值的二进制表达对比(上下两个二进制间不同的位被 ! 高亮出来,表格的最右 列显示了有多少位不相同) 32-bit Python build 00000000000000000000000000000001 != 0 1.0 00000000000000000000000000000001 ------------------------------------------------ 1.0 00000000000000000000000000000001 ! !!! ! !! ! ! ! ! !! !!! != 16 1.0001 00101110101101010000101011011101 ------------------------------------------------ 1.0001 00101110101101010000101011011101 !!! !!!! !!!!! !!!!! !! ! != 20 1.0002 01011101011010100001010110111001 ------------------------------------------------ 1.0002 01011101011010100001010110111001 ! ! ! !!! ! ! !! ! ! ! !!!! != 17 1.0003 00001100000111110010000010010110 ------------------------------------------------ 用来计算示例 3-16 的程序见于附录 A。尽管程序里大部分代码都是用来整理输出格 式的,考虑到完整性,我还是把全部的代码放在示例 A-3 中了。 从 Python 3.3 开始,str、bytes 和 datetime 对象的散列值计算过程中多 了随机的“加盐”这一步。所加盐值是 Python 进程内的一个常量,但是每次启动 Python 解释器都会生成一个不同的盐值。随机盐值的加入是为了防止 DOS 攻击 而采取的一种安全措施。在 __hash__ 特殊方法的文档 (https://docs.python.org/3/reference/datamodel.html#object.__hash__) 里有相关的详 细信息。 了解对象散列值相关的基本概念之后,我们可以深入到散列表工作原理背后的算法 了。 02. 散列表算法 为了获取 my_dict[search_key] 背后的值,Python 首先会调用 hash(search_key)来计算 search_key 的散列值,把这个值最低的几位数字当作偏移量,在散列表里查找表元(具体取几位,得看当前散列表的大小)。若找到的表元是空的,则抛出KeyError 异常。若不是空的,则表元里会有一对 found_key:found_value。这时候 Python 会检验 search_key == found_key 是否为真,如果它们相等的话,就会返回 found_value。如果 search_key 和 found_key 不匹配的话,这种情况称为散列冲突。发生这种情况是因为,散列表所做的其实是把随机的元素映射到只有几位的数字上,而散列表本身的索引又只依赖于这个数字的一部分。为了解决散列冲突,算法会在散列值中另外再取几位,然后用特殊的方法处理一下,把新得到的数字再当作索引来寻找表元。若这次找到的表元是空的,则同样抛出 KeyError;若非空,或者键匹配,则返回这个值;或者又发现了散列冲突,则重复以上的步骤。图 3-3 展示了这个算法的示意 图。图 3-3:从字典中取值的算法流程图;给定一个键,这个算法要么返回一个值,要么抛出 KeyError 异常添加新元素和更新现有键值的操作几乎跟上面一样。只不过对于前者,在发现空表元的时候会放入一个新元素;对于后者,在找到相对应的表元后,原表里的值对象会被替换成新值。 另外在插入新值时,Python 可能会按照散列表的拥挤程度来决定是否要重新分配内存为它扩容。如果增加了散列表的大小,那散列值所占的位数和用作索引的位数都会随之增加,这样做的目的是为了减少发生散列冲突的概率。表面上看,这个算法似乎很费事,而实际上就算 dict 里有数百万个元素,多数的搜索过程中并不会有冲突发生,平均下来每次搜索可能会有一到两次冲突。在正常情况下,就算是最不走运的键所遇到的冲突的次数用一只手也能数过来。了解 dict 的工作原理能让我们知道它的所长和所短,以及从它衍生而来的数据类型 详细解释