3.1 泛映射类型
collections.abc
模块中有Mapping
和MutableMapping
这两个抽象基类,它们的作用是为字典和集合dict
和其他类似的类型定义形式接口。
from collections import abc
my_dict = {}
print(isinstance(my_dict, abc.Mapping)) # isinstance 可以用来判定某个数据是不是广义上的映射类型
标准库里的所有映射类型都是利用dict
来实现的,因此只有可散列的数据类型才能用作这些映射里的键(值无此限制)。
可散列的数据类型
基本特征:
- 该对象的生命周期中,散列值不变
- 实现了
__hash__()
方法、__qe__()
方法, 这样才能跟其他键做比较 - 如果两个可散列对象是相等的,那么它们的散列值一定是一样的
- 一般来讲用户自定义的类型的对象都是可散列的,散列值就是它们的id() 函数的返回值,所以所有这些对象在比较的时候都是不相等的。
可散列的数据类型:
- 原子不可变数据类型(str、bytes 和数值类型),frozenset (只能容纳可散列类型)
- 所有元素都是可散列类型的元组
tt = (1, 2, (30, 40))
print(hash(tt)) # -3907003130834322577
tl = (1, 2, [30, 40])
print(hash(tl)) # TypeError: unhashable type: 'list'
tf = (1, 2, frozenset([30, 40]))
print(hash(tf)) # 5149391500123939311
字典的不同创建方式
a = dict(one=1, two=2, three=3)
b = {'one': 1, 'two': 2, 'three': 3}
c = dict(zip(['one', 'two', 'three'], [1, 2, 3]))
d = dict([('two', 2), ('one', 1), ('three', 3)])
e = dict({'three': 3, 'one': 1, 'two': 2})
print(a == b == c == d == e) # True
3.2 字典推导
# 示例 1 字典推导的应用
DIAL_CODES = [(86, 'China'),(91, 'India'),(1, 'United States'),(62, 'Indonesia')]
country_code = {country: code for code, country in DIAL_CODES}
print(country_code) # {'China': 86, 'India': 91, 'United States': 1, 'Indonesia': 62}
code_country = {code: country.upper() for country, code in country_code.items() if code < 66}
print(code_country) # {1: 'UNITED STATES', 62: 'INDONESIA'}
3.3 常见的映射方法
update 鸭子类型
update 方法处理参数m 的方式,是典型的“鸭子类型”。
- 函数首先检查m是否有keys 方法,如果有,那么update 函数就把它当作映射对象来处理。否则,函数会退一步,转而把m 当作包含了键值对(key, value) 元素的迭代器。
- Python 里大多数映射类型的构造方法都采用了类似的逻辑,因此你既可以用一个映射对象来新建一个映射对象,也可以用包含(key, value) 元素的可迭代对象来初始化一个映射对象。
tinydict = {'Name': 'Runoob', 'Age': 7}
tinydict2 = {'Sex': 'female', 'Age': 9}
tinydict.update(tinydict2)
print(tinydict) # {'Name': 'Runoob', 'Age': 9, 'Sex': 'female'}
用setdefault处理找不到的键
d.get(k, default)
可用来代替d[k]
,给找不到的键一个默认的返回值。d.setdefault(key, []).append(new_value)
当key不存在时,新增该键值对。相比for循环,减少查询次数。
student_a = {'Name': 'Runoob', 'Age': 9, 'Sex': 'female'}
student_a.setdefault("Age", 16) # 如果key存在,则不修改值
student_a.setdefault("school", "NJU")
student_a.setdefault("girlfriend", []).append("Ann")
print(student_a) # {'Name': 'Runoob', 'Age': 9, 'Sex': 'female', 'school': 'NJU', 'girlfriend': ['Ann']}
# enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和下标,多用于for循环中。
seasons = ['Spring', 'Summer', 'Fall', 'Winter']
print(list(enumerate(seasons))) # [(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
print(list(enumerate(seasons, start=1))) # [(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]
3.4 映射的弹性键查询
3.4.1 defaultdict:处理找不到的键的一个选择
上述的setdefault方法在每次调用时都要我们手动指定默认值,那有没有什么办法能方便一些,在键不存在时,直接返回我们指定的默认值?两个常用的方法是:①使用defaultdict
类;②自定义一个dict
子类,在子类中实现__missing__
方法,而这个方法又有至少两种方法。
collections.defaultdict( [default_factory[, …] ])
该函数返回一个类似字典的对象。defaultdict
是Python内建字典类(dict)的一个子类,它重写了方法_missing_(key)
,增加了一个可写的实例变量default_factory,
实例变量default_factory
被missing()
方法使用,如果该变量存在,则用以初始化构造器,如果没有,则为None。其它的功能和dict一样。
import collections
my_dict = collections.defaultdict(list)
my_dict[key].append(new_value) # 无需判断键key是否存在于my_dict中
在实例化defaultdict时,需要给构造方法提供一个可调用对象(实现了__call__
方法的对象),这个可调用对象存储在defaultdict
类的属性default_factory
中,当__getitem__
找不到所需的键时就会通过default_factory
来调用这个可调用对象来创建默认值。
上述代码中my_dict[key]
的内部过程如下(假设key是新键):
- 调用
list()
来创建一个新列表; - 把这个新列表作为值,
key
作为它的键,放到my_dict
中; - 返回这个列表的引用。
如果在实例化defaultdict
时未指定default_factory
,那么在查询不存在的键时则会触发KeyError
;defaultdict
中的default_factory
只会在__getitem__
里被调用,在其它的方法里完全不会发挥作用!比如,dd是个defaultdict,k是个不存在的键,dd[k]
这个表达式则会调用default_factory,并返回默认值,而dd.get(k)
则会返回None。
3.4.2 特殊方法__missing__
- 当
__getitem__
找不到键的时候,Python 就会自动调用__missing__
,而不是抛出一个KeyError 异常。(__missing__
方法只会被__getitem__
调用,比如在表达式d[k] 中)。 __getitem__
调用__missing__
,__missing__
调用default_factory
。
# StrKeyDict0 在查询的时候把非字符串的键转换为字符串
class StrKeyDict0(dict):
def __missing__(self, key):
if isinstance(key, str):
raise KeyError(key)
return self[str(key)]
def get(self, key, default=None):
try:
return self[key]
except KeyError:
return default
def __contains__(self, key): # k in d 会调用__contains__,但从dict 继承的__contains__ 在找不到键时不会调用__missing__方法
return key in self.keys() or str(key) in self.keys()
```