流畅的Python -- 第三章 字典与集合
[TOC]
泛映射类型(key-value)
这里提到了一个可散列的概念,可散列对象需要实现一个__hash__方法,会有一个可散列值,这个值在生命周期中是不变的,大概也就是说需要是原子不可变数据类型(str、bytes和数值类型),一般来讲用户自定义类的对象都是可散列的,散列值是他们id()函数的返回值
只有可散列的数据才能作为字典的key
dict(one=1, two=2, three=3)
{'one': 1, 'two': 2, 'three': 3}
dict(zip(['one', 'two', 'three'], [1, 2, 3]))
dict([('two', 2), ('one', 1), ('three', 3)])
dict({'three': 3, 'one': 1, 'two': 2})
构建字典的几种方式,实现方式太多也不是好事情
字典推导
DIAL_CODES = [(86, 'China')]
{country: code for code, country in DIAL_CODES}
写法有些迷惑,用到的时候再看吧,和列表推导相似
常见的映射方法
用setdefault处理找不到的键
d.get(k, default)通过这个方法可以为找不到的键一个默认的返回值
# 方式一:
if key not in my_dict:
my_dict[key] = []
my_dict[key].append(new_value)
# 方式二:
my_dict.setdefault(key, []).append(new_value)
方式一需要至少两次查询,如果key不存在需要三次查询,而方式二只需要一次查询
映射的弹性查询
defaultdict 处理找不到的键
index = collections.defaultdict(list)
index[key].append(new_value)
defaultdict会按照定义为不存在的key做初始化,所以第二句代码总能成功执行
如果创建的时候没有指定default_factory,在查询不存在的key的时候会触发KeyError
defaultdict的初始化操作是在__getitem__方法中被调用的,如果通过index.get(key)的方式就会返回None
__missing__方法
所有的映射类型在处理找不到键的时候,都会尝试调用__missing__方法,而dict中只是没有去做实现所以直接触发了KeyError,我们可以继承dict然后实现__missing__方法即可模拟defaultdict的功能
继承dict以自定义功能
通过覆盖父类的方法来实现key忽略类型来获取数据(d[1]和d[‘1’]获取到同一个value)
字典的变种
collections.OrderedDict
一个有序的字典,通过my_odict.popitem(last=False)可以实现队列的功能
collections.ChainMap
没整明白
collections.Counter
ct = collections.Counter('abracadabra')
ct # Counter({'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1})
ct.most_common(2) # [('a', 5), ('b', 2)]
colllections.UserDict
使用纯Python把dict写了一遍,这个类不直接提供功能,而是用于用户继承已自定义子类
子类化UserDict
UserDict和dict的区别上面也大概说了,如果我们想要一个定制化的字典类,那通过继承UserDict来实现要比继承dict要方便很多(UserDict也不是dict的子类)
不可变映射类型
标准库中所有的映射类型都是可变的,如果不希望某些数据在使用中被篡改,可以借助MappingProxyType来实现
from types import MappingProxyType
d = {a:1}
d_proxy = MappingProxyType(d)
这里d_proxy只读,而修改d的内容d_proxy内容会同步更新,可以将d设置为私有,而暴露d_proxy给用户
论集合
即set,在Python里面出现比较晚,用得也比较少,特性就是去重
# needles和haystack是两个集合
found = len(needles & haystack)
found = 0
for n in needles:
if n in haystack:
found += 1
比较逗的是书上说的是方法一比方法二要快,方法一要求两个序列都是集合,方法二只要是可迭代对象,但如果两个都不是set的时候,虽然可以做强转但会涉及到对象转换的成本,结论说,如果两个对象中有一个已经是set都可能会比第二种方法快
