没有合适的资源?快使用搜索试试~ 我知道了~
1. 散列值和相等性 2. 散列表算法 1. 键必须是可散列的 2. 字典在内存上的开销巨大 3. 键查询很快 4. 键的次序取决于添加顺序 5. 往字典里添加
资源详情
资源评论
资源推荐
字典和集合
字典和集合的实现都依赖于散
表
泛映射类型
collections.abc 模块中有
Mapping 和 MutableMapping
这两个抽象基类
为 dict 和其他类似的类型定义形
式接
然,抽象映射类型般会
直接继承这些抽象基类,它们会
直接对 dict 或是
collections.User.Dict 进扩
展。
这些抽象基类的主要作是作为
形式化的档,它们定义构建
个映射类型所需要的最基本的
接。
标准库的所有映射类型都是
dict 来实现的,因此它们有个
共同的限制,即只有可散的数
据类型才能作这些映射的键
(只有键有这个要求,值并需
要是可散的数据类型) 。
如果个对象是可散的,那么
在这个对象的命周期中,它的
散值是变的,且这个对象
需要实现 __hash__() 法。
另外可散对象还要有 __qe__()
法, 这样才能跟其他键做
较。
字典推导
字典推导(dictcomp)可以从任
何以键值对作为元素的可迭代对
象中构建出字典。
常的映射法
通过查找来插新值
update 法处参数 m 的
式,是典型的“鸭类型” 。
可以个映射对象来新建个
映射对象,也可以包含 (key,
value) 元素的可迭代对象来初始
化个映射对象。
setdefault处找到的键
可以 d.get(k, default) 来代替
d[k],给找到的键个默认的
返回值(这处 KeyError 要
少)
dict.get 并是处找到的键
的最好法
dict.setdefault
映射的弹性键查询
单纯地查找取值
有时候为起,就算某个
键在映射存在,我们也希望
在通过这个键读取值的时候能得
到个默认值。
defaultdict:处找到的键的
个选择
在实化个 defaultdict 的时
候,需要给构造法提供个可
调对象,这个可调对象会在
__getitem__ 碰到找到的键的
时候被调,让 __getitem__ 返
回某种默认值。
dd = defaultdict(list),键’new-
key’存在时,dd[‘new-key’]会
进:
(1) 调 list() 来建个新
表。
(2) 把这个新表作为值,'new-
key' 作为它的键,放到 dd 中。
(3) 返回这个表的引。
如果在创建 defaultdict 的时候
没有指定 default_factory,查询
存在的键会触发KeyError。
特殊法 __missing__。它会在
defaultdict 遇到找到的键的时
候调 default_factory
所有的映射类型在处找到的
键的时候,都会牵扯到
__missing__ 法。
__missing__ 法只会被
__getitem__ 调(如在表达
式 d[k] 中) 。
重写__missing__法时要注意
__getitem__和__contains__
法,以免被递归调。
字典的变种
collections.OrderedDict
这个类型在添加键的时候会保持
顺序,因此键的迭代次序总是
致的。
collections.ChainMap
该类型可以容纳数个同的映射
对象,然后在进键查找操作的
时候,这些对象会被当作个整
体被逐个查找,直到键被找到为
。
这个功能在给有嵌套作域的语
做解释的时候很有,可以
个映射对象来代表个作
域的上下。
collections.Counter
这个映射类型会给键准备个整
数计数。
每次新个键的时候都会增加
这个计数。所以这个类型可以
来给可散表对象计数,或者
是当成多重集来
colllections.UserDict
把标准 dict 纯 Python 实现
遍。
类化UserDict
就创造定义映射类型来说,以
UserDict 为基类,总以普通的
dict 为基类要来得。
倾向于从 UserDict 是从
dict 继承的主要原因是,后者有
时会在某些法的实现上些
捷径,导致我们得在它的
类中重写这些法,但是
UserDict 就会带来这些问题。
UserDict 并是 dict 的类,
但是 UserDict 有个叫作data
的属性,是 dict 的实,这个属
性实际上是 UserDict 最终存储
数据的地。
UserDict 的类就能在实现
__setitem__ 的时候避免必要
的递归,也可以让 __contains__
的代码简洁。
因为 UserDict 继承的是
MutableMapping,所以
StrKeyDict 剩下的那些映射类
型的法都是从 UserDict、
MutableMapping 和 Mapping
这些超类继承来的。
MutableMapping.update
Mapping.get
可变映射类型
types 模块中引个封装类
名叫 MappingProxyType
如果给这个类个映射,它会返
回个只读的映射视图。虽然是
个只读视图,但是它是动态的。
集合论
集合的本质是许多唯对象的聚
集。因此,集合可以于去重
集合中的元素必须是可散的,
set 类型本身是可散的,但
是 frozenset 可以。
速度极快的查找功能(这也得归
功于它背后的散表)
集合还实现很多基础的中缀运
算符
a | b 返回的是它们的合集
a & b 得到的是交集
a - b 得到的是差集
集合字
除空集之外,集合的字——
{1}、{1, 2},等等
如果是空集,那么必须写成 set()
的形式。
如果只是写成 {} 的形式,跟以前
样,你创建的其实是个空字
典。
像 {1, 2, 3} 这种字法相
于构造法(set([1, 2, 3]))要
快且读。
dis.dis(反汇编函数)来看看
两个法的字节码的同
Python 没有针对 frozenset
的特殊字法,我们只能采
构造法。
集合的操作
有些运算符和法会对集合做就
地修改(像 &=、
difference_update,等等)
frozenset 也会实现这些操作
dict和set的背后
字典中的散表
散表其实是个稀疏数组(总
是有空元素的数组称为稀疏数
组) 。
散表的单元通常叫作表元
(bucket) 。在 dict 的散表
当中,每个键值对都占个表
元,每个表元都有两个部分,
个是对键的引,另个是对值
的引。
所有表元的致,所以可以
通过偏移来读取某个表元。
如果要把个对象放散表,
那么先要计算这个元素键的散
值。Python 中可以hash()
法来做这件事情
1. 散值和相等性
内置的 hash() 法可以于所
有的内置类型对象。如果是定
义对象调 hash() 的话,实际
上运的是定义的
__hash__。
如果两个对象在较的时候是相
等的,那它们的散值必须相
等,否则散表就能正常运
。
为让散值能够胜任散表索
引这,它们必须在索引空
间中尽分散开来。
在最想的状况下,越是相似但
相等的对象,它们散值的差
别应该越。
2. 散表算法
为获取 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
匹配的话,这种情况称为散
冲突。
因为,散表所做的其实是把随
机的元素映射到只有位的数字
上,散表本身的索引只依
赖于这个数字的部分。
为解决散冲突,算法会在散
值中另外再取位,然后特
殊的法处下,把新得到的
数字再当作索引来寻找表元。
添加新元素和新现有键值的操
作乎跟上样
只过对于前者,在发现空表元
的时候会放个新元素
对于后者,在找到相对应的表元
后,原表的值对象会被替换成
新值。
另外在插新值时,Python 可
能会按照散表的拥挤程度来决
定是否要重新分配内存为它扩
容。
如果增加散表的,那散
值所占的位数和作索引的位
数都会随之增加,这样做的的
是为减少发散冲突的概
率。
dict的实现及其导致的结果
1. 键必须是可散的
个可散的对象必须满
(1) 持 hash() 函数,并且通过
__hash__() 法所得到的散值
是变的。
(2) 持通过 __eq__() 法来检
测相等性。
(3) 若 a == b 为真,则 hash(a)
== hash(b) 也为真。
所有由户定义的对象默认都
是可散的,因为它们的散值
由 id() 来获取,且它们都是
相等的。
如果你实现个类的 __eq__
法,并且希望它是可散的,
那么它定要有个恰当的
__hash__ 法,保证在 a == b
为真的情况下 hash(a) ==
hash(b) 也必定为真。
2. 字典在内存上的开销巨
由于字典使散表,散
表必须是稀疏的,这导致它在
空间上的效率低下。
如果你需要存放数巨的记
录,那么放在由元组或是具名元
组构成的表中会是较好的选
择;最好要根据 JSON 的
格,由字典组成的表来存放
这些记录。
元组取代字典就能节省空间的
原因有两个
避免散表所耗费的空间
需把记录中字段的名字在每个
元素都存遍
3. 键查询很快
dict 的实现是典型的空间换时
间:字典类型有着巨的内存开
销,但它们提供视数据
的快速访问——只要字典能被
装在内存。
4. 键的次序取决于添加顺序
有相同键值对的字典进较时
是相等的,但键的顺序可能
样。
当往 dict 添加新键发散
冲突的时候,新键可能会被安
排存放到另个位置。
5. 往字典添加新键可能会改变
已有键的顺序
论何时往字典添加新的键,
Python 解释都可能做出为字
典扩容的决定。
结果就是要新建个的散
表,并把字典已有的元素添加
到新表。
可能会发新的散冲突,导致
新散表中键的次序变化。
如果你在迭代个字典的所有键
的过程中同时对字典进修改,
那么这个循环很有可能会跳过
些键——甚是跳过那些字典中
已经有的键。
如果想扫描并修改个字典,最
好分成两步来进
先对字典迭代,以得出需要添
加的内容,把这些内容放在个
新字典
迭代结束之后再对原有字典进
新。
set的实现以及导致的结果
set 和 frozenset 的实现也依赖
散表,但在它们的散表存
放的只有元素的引(就像在字
典只存放键没有相应的
值) 。
特点
集合的元素必须是可散的。
集合很消耗内存。
可以很效地判断元素是否存在
于某个集合。
元素的次序取决于被添加到集合
的次序。
往集合添加元素,可能会改变
集合已有元素的次序。
结
多数映射类型都提供两个很
强的法:setdefault 和
update。
setdefault 法可以来新字
典存放的可变值(如
表) ,从避免重复的键搜
索。
update 法则让批新成为
可能,它可以来插新值或者
新已有键值对,它的参数可以
是包含(key, value) 这种键值对
的可迭代对象,或者关键字参
数。
collections.abc 模块提供
Mapping 和 MutableMapping
这两个抽象基类,它们,我
们可以进类型查询或者引。
优游的鱼
- 粉丝: 73
- 资源: 316
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功
评论0