流畅的python——字典和集合
泛映射类型
标准库中的所有映射类型都是利用dict实现的,只有可
散列的数据类型才能用作键。
可散列:
- 如果一个对象是可散列的,在生命周期中,散列值是不变的。
- 需要实现__hash__()方法和__qe__()方法
- 不可变数据类型(str,bytes和数值类型)都是可散列的
- 如果元组中的元素都是不可变的,则该元组是可散列的
setdefault和defaultdict
setdefault
defaultdict
import collections
dict = collections.defaultdict(list)
如果在创建defaultdict是没有指定default_factory,查询不存在的键会触发keyError,defauldict里的default_factory只会再__getitem__()里被调用(即dict[key]时会,dict.get(),dict.contains()不会)。
特殊方法__missing__
class StrKeyDict0(dict):
def __missing__(self,key):
if isinstance(key,str):
raise KeyError(key)
return self[str(key)) //如果输入的数字也算合法
def get(self,key,default=Node):
try:
return self[key]
except KeyError:
return default
def __contains__(self,key):
return key in self.keys() or str(key) in self.keys()
字典的变种
- collections.OrderDict
popitem(last=[Ture])默认删除并返回字典的最后一个元素.move_to_end(). 可以用作LRU? - collections.ChaniMap:
可以容乃数个不同的映射对象,当进行键查找操作室,这些对象会被当做一个整体被逐个查找,直到键被找到位置。这个功能在给有嵌套作用域的语言做解释器的时候很有用。
import builtins
pylookup = ChainMap(locals(), globals(), vars(builtins))
- collections.Counter
- collections.UserDict
不可变映射类型
需求:标准库里所有的映射类型都是可变的,但有时不能让用户错误的修改某个映射。
types模块中引入了MappingProxyType
输入:映射
输出:该映射的只读视图。
说明:虽然只是只读视图,但如果对原映射做了改动,我们通过这个视图可以观察到。
from types import MappingProxyType
d = {1:'A'}
d_proxy = MappingProxyType(d)
d_proxy[2]='x' # 会报错
d[2] = 'x' # ok,通过d_proxy可以观察到
集合
python中的集合指:set和frozenset
集合中的元素必须是可散列的,set类型本身是不可散列的,但frozenset是可以的。
s.union,s.issubset,s.issuperset,s.difference
s.discard(e) 如果s中有e这个元素,则把e移除
s.remove(e)
dict和set的背后
dict和set的效率
用in运算符在5个不同大小的haystack字典里搜索1000个元素所需的时间:
字典中的散列表
散列表是一个稀疏数组,散列表中的单元叫做表元(bucket)。在dict的散列表中,每个键值对都占用一个表元,每个表元有两部分,一部分是对键的引用,另一个是对值的引用。所有表元的大小一致,可以通过偏移量来读取某个表元。
python会设法保证大概有1/3的表元是空的,在快要达到阈值是,原有的散列表会被复制到一个更大的空间里。
用hash()方法计算元素键的散列值。
- 散列值和相等性:如果两个对象在比较时是相等的,那门他们的hash值应该相等。
- 散列表算法:
另外在插入新值时,Python 可能会按照散列表的拥挤程度来决定是否要重新分配内存 为它扩容。如果增加了散列表的大小,那散列值所占的位数和用作索引的位数都会随 之增加,这样做的目的是为了减少发生散列冲突的概率
dict的实现及其导致的结果
- 键必须是可散列的
- 字典在内存上开销巨大:由于字典使用了散列表,而散列表优势稀疏的,导致空间效率低。如果要存储数量巨大的记录,最好使用元组(避免了散列表耗费的空间,且无需把记录中的字段的名字在每个元组里都存一遍)
- 键查询很快
- 键的词语取决于添加顺序
- 向字典里添加新建可能会改变已有键的顺序
set的实现及其导致的结果
set 和 frozenset 的实现也依赖散列表,但在它们的散列表里存放的只有元素的引用 (就像在字典里只存放键而没有相应的值)。在 set 加入到 Python 之前,我们都是把字 典加上无意义的值当作集合来用的。
导致的结果同dict