3.1字典构造方法
>>> 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({'one':1, 'two':2, 'three':3})
>>> f = dict({'one':1, 'three':3}, two=2)
>>> a == b == c == d == e == f
True
3.2 字典推导
>>> DIAL_CODES = [
... (86, 'China'),
... (91, 'India'),
... (1, 'United States'),
... (62, 'Indonesia'),
... (55, 'Brazil'),
... ]
>>> country_code = {country: code for code, country in DIAL_CODES}
>>> country_code
{'China': 86, 'India': 91, 'United States': 1, 'Indonesia': 62, 'Brazil': 55}
>>> {code: country.upper() for country, code in country_code.items() if code < 66}
{1: 'UNITED STATES', 62: 'INDONESIA', 55: 'BRAZIL'}
3.3常见的映射方法
用setdefault处理找不到的键
"""创建从一个单词到其出现情况的映射"""
import sys
import re
WORD_RE = re.compile(r'\w+')
index = {}
with open(sys.argv[1], encoding='utf-8') as fp:
for line_no, line in enumerate(fp, 1):
for match in WORD_RE.finditer(line):
word = match.group()
column_no = match.start()+1
location = (line_no, column_no)
index.setdefault(word, []).append(location)
# 以字母顺序打印出结果
for word in sorted(index, key=str.upper):
print(word, index[word])
结果:
a [(17, 48), (18, 53)]
Although [(9, 1), (14, 1), (16, 1)]
ambiguity [(12, 16)]
and [(13, 23)]
are [(19, 12)]
aren [(8, 15)]
at [(14, 38)]
bad [(17, 50)]
be [(13, 14), (14, 27), (18, 50)]
beats [(9, 23)]
Beautiful [(1, 1)]
better [(1, 14), (2, 13), (3, 11), (4, 12), (5, 9), (6, 11), (15, 8), (16, 25)]
break [(8, 40)]
cases [(8, 9)]
complex [(3, 23)]
Complex [(4, 1)]
complicated [(4, 24)]
counts [(7, 13)]
dense [(6, 23)]
...
3.4 映射的弹性键查询
有时候为了方便起见,就算某个键在映射里不存在,我们希望在通过这个键读取值的时候能得到一个默认值。有两种方法:一是通过defaultdict,另外一个是定义一个dict的子类,在子类中实现__missing__方法。
3.4.1 defaultdict:处理找不到的键的一个选择
在用户创建defaultdict对象的时候,需要给它配置一个为找不到的键创造默认值的方法。
具体而言,在实例化一个defaultdict的时候,需要给构造方法提供一个可调用对象,这个对象会在__getitem__找不到键的时候被调用,让__getitem__返回某种默认值。
"""创建从一个单词到其出现情况的映射"""
import sys
import re
import collections
WORD_RE = re.compile(r'\w+')
index = collections.defaultdict(list)
with open(sys.argv[1], encoding='utf-8') as fp:
for line_no, line in enumerate(fp, 1):
for match in WORD_RE.finditer(line):
word = match.group()
column_no = match.start()+1
location = (line_no, column_no)
index.setdefault(word, []).append(location)
# 以字母顺序打印出结果
for word in sorted(index, key=str.upper):
print(word, index[word])
3.4.2 特殊方法__missing__
所有的映射类型在处理找不到的键的时候,都会牵扯到__missing__方法。如果有一个类继承了dict,然后这个子类提供了__missing__方法,那么在__getitem__找到键的时候,Python就会自动调动它,而不是抛出一个KeyError异常。
d = StrKeyDict0([('2', 'two'), ('4', 'four')])
#测试d[k]
d[2]
'two'
d[4]
'four'
d[1]
Traceback (most recent call last):
...
KeyError: '1'
#测试d.get(key)
d.get('2')
'two'
d.get('4')
'four'
d.get(1, 'N/A')
'N/A'
#测试 in
2 in d
True
1 in d
False
3.5字典的变种
collections.ChainMap
该类型可以容纳数个不同的映射对象,然后在进行键查找操作的时候,这些对象会被当作一个整体被逐个查询,直至键被找到为止。
>>> import collections
>>> a = {'a':1}
>>> b = {'b':2}
>>> c = {'c':3}
>>> abc = collections.ChainMap(a, b, c)
>>> abc['a']
1
>>> abc['b']
2
>>> abc['c']
3
collections.Counter
这个映射类型会给键准备一个整数计数器。每次更新一个键的时候都会增加这个计数器。
>>> import collections
>>> ct = collections.Counter('abracadabra')
>>> ct
Counter({'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1})
>>> ct.update('aaaaaazzz')
>>> ct
Counter({'a': 11, 'z': 3, 'b': 2, 'r': 2, 'c': 1, 'd': 1})
>>> ct.most_common(2)
[('a', 11), ('z', 3)]
3.6 子类化UserDict
就创造自定义映射类型来说,以UserDict为基类,比以普通的dict为基类要来的方便
主要原因是dict的某些方法的实现走了一些捷径,导致我们不得不在它的子类中重写这些方法。
import collections
class StrKeyDict(collections.UserDict):
def __missing__(self, key):
if isinstance(key, str):
raise KeyError(key)
return self[str(key)]
def __contains__(self, item):
return str(key) in self.data
def __setitem__(self, key, item):
self.data[str(key)] = item
3.7 不可变映射类型
标准库里所有的映射类型都是可以变的,但是有时候你会有这样的需求,不能让用户错误地修改某个映射。
从Python3.3开始,types模块中引入了一个封装类名叫MappingProxyType。如果给这个类一个映射,它会返回一个只读的映射视图。虽然是个只读的映射视图,但是它是动态的。这意味着如果对原映射做出了改动,我们通过这个视图可以观察到,但是无法通过这个视图对原映射做出修改。
>>> from types import MappingProxyType
>>> d = {1:'A'}
>>> d_proxy = MappingProxyType(d)
>>> d_proxy
mappingproxy({1: 'A'})
>>> type(d_proxy)
<class 'mappingproxy'>
>>> d_proxy[1]
'A'
>>> d_proxy[1] = 'B'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'mappingproxy' object does not support item assignment
>>> d_proxy[2] = 'x'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'mappingproxy' object does not support item assignment
>>> d[2] = 'B'
>>> d_proxy
mappingproxy({1: 'A', 2: 'B'})
>>> d_proxy[2]
'B'