1、collections.abc模块中有Mapping和MutableMapping两个抽象基类,它们的作用是为dict和其他类似的类型定义形式接口。
2、标准库里的所有映射类型都是利用dict实现的,只有可散列(hashable)的数据类型才能用作这些映射里的键。如果一个对象是可散列的,那么在这个对象的生命周期中,它的散列值是不变的,而且这个对象需要实现__hash__()
方法进行散列,也要实现__eq__()
方法进行键的比较。python的原子不可变类型(str、bytes和数值类型)是可散列的,一个元组是可散列的当且仅当其包含的元素都是可散列的。所以严格来说不可变类型不都是可散列的,元组不一定。
3、和列表推导式类似,字典也有推导构建法:
4、处理字典可能找不到键的情况:
- 使用
setdefault()
方法,如下图。这样做的好处是减少了查询字典的次数,如果键不存在,不使用setdefault()
就需要查三次字典。
使用
collections.defaultdict
字典,提供一个可调用对象作为参数。例如当用语句index = collections.defaultdict(list)
创建字典后,执行index['new_key']
找不到键,则会自动调用list()
方法生成空列表,并以new_key
为键添加到字典中,最后返回该列表的引用。上述两种方法的原理都是实现了
__missing__
方法,字典使用__getitem__
方法进行查询,当找不到键时,如果实现了__missing__
方法,__getitem__
会直接调用,否则抛出异常。原始的dict没有实现__missing__
方法,但它知道有了__missing__
方法就可以用。所以第三种方法是创建dict的子类,实现__missing__
方法。
5、types模块有一个封装类MappingProxyType,用于返回一个字典的不可变的视图,这个视图是动态的,原字典改变视图也会随之改变,所以如果不想字典在使用过程中被修改,可能会用到这个类。
6、python的集合类型有set和frozenset,集合中的元素必须是可散列的,set本身是不可散列的,但frozenset是可散列的。
7、set集合除了可以从列表生成,还可以使用集合字面量(set literals)定义。比如s=set([1,2,3])
和s={1,2,3}
是等价的,但字面量方式更高效。需要注意,空集只能用s=set()
生成,字面量形式的s={}
是生成空字典。此外,frozenset不支持字面量操作,所以只能从列表生成。由于支持字面量定义,set集合还能用推导式生成,和列表推导式类似,只不过两端是大括号。
8、集合类的继承关系如下:
9、dict是用散列表实现的,理论上只要字典不超过内存大小,查询操作耗费的时间都能忽略不计。散列表的查询流程如下,其中bucket指的是散列表中的单元,即表元,dict的每个键值对占用一个表元。由于流程中使用散列值的一部分进行匹配,可能会发生匹配部分相同但整体键不相等,这就叫散列冲突(hash collision),这时就会取散列值的另一部分再进行匹配。虽然看似效率低下,但散列函数的特性是不相等的对象散列值差别很大,所以实际上发生散列冲突的概率非常小。
10、字典提供了无视数据量大小的快速访问,代价是空间效率低下,因为散列表必须是稀疏的,需要占用大量空间。此外,不能对字典同时进行迭代和修改,如果往字典里添加新键,解释器可能会做出为字典扩容的决定,将当前散列表迁移到更大的散列表中,在这个过程中对字典进行迭代,可能会出现问题。
11、set和frozenset集合也是基于散列表实现的,只不过表元是对单个元素的引用。所以集合类型也有时间效率高、空间效率低的特点。