4. 字典
4.1 字典推导式
字典推导可以从任何以键值对作为元素的可迭代对象中构造字典。推导式不止for
循环,还可以加其他简单条件:
In [11]: a='abcdef'
In [13]: b=[i for i in range(6)]
In [17]: ab=list(zip(a,b))
In [18]: ab
Out[18]: [('a', 0), ('b', 1), ('c', 2), ('d', 3), ('e', 4), ('f', 5)]
In [19]: d={l:d for l,d in ab}
In [20]: d
Out[20]: {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4, 'f': 5}
In [21]: dd={l:d for l,d in ab if d>3}
In [22]: dd
Out[22]: {'e': 4, 'f': 5}
4.2常见方法
方法 | 说明 |
---|---|
d.clear() | |
d.default_factory | 在__missing__ 函数中被调用的函数,用于给未找到的元素设置值。default_factory 并不是一个方法,而是一个可调用对象(callable),它的值在defaultdict 初始化的时候用用户指定 |
d.get(k,[default]) | 返回键k 对应的值,若没有返回default ,或者None |
d.item() | 返回所有键值对,可以用来遍历字典 |
d.keys() | |
d.pop(k,[default]) | |
d.popitem() | |
d.setdefault(k,[default]) | 如果字典存在键 key ,返回它的值。如果不存在,插入值为 default 的键 key ,并返回 default 。 default 默认为 None 。 |
4.2.1 用setdefault处理找不到的键
当字典d[k]不能找到正确的键的时候,python就会抛出异常,这个行为符合python所信奉的“快速失败”哲学。也许每个python程序员都知道可以用d.get(k,default)来代替d[k],给找不到的键返回一个默认值(这比处理KeyError要方便不少)。但是要更新某个键对应的值的时候,不管使用__getitem__()还是get()都不会自然,而且效率低。看下边的示例index0.py:
统计index0.py文件中单词、数字出现的位置
import sys
import re
WORD_RE=re.compile(r'\w+')
print(sys.argv[0])
index={}
with open(sys.argv[0],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)
occurrences=index.get(word,[]) #1
occurrences.append(location) #2
index[word]=occurrences #3
for word in sorted(index,key=str.upper):
print(word,index[word])
1 一次查询。查询word出现的位置,如果还没有他的记录返回[].
3 把新的列表列表放入字典,这又涉及到一次查询。
示例index1.py使用了dict.setdefault,只用一行就解决了获取和更新单词出现情况列表:
import sys
import re
WORD_RE=re.compile(r'\w+')
print(sys.argv[0])
index={}
with open(sys.argv[0],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)#1
for word in sorted(index,key=str.upper):
print(word,index[word])
1 获取单词出现情况列表,如果单词不存在,把单词和一个空列表放入字典,然后返回这个空列表,这样就能在不进行第二次查找的情况下更新列表了。
4.3 处理查不到的键
有时候就算某个键在字典中不存在,我们也希望在通过这个键取值的时候能得到一个默认值。
有两个办法可以达到这个目的,一是通过defaultdict这个类型而不是普通的dict,另一个是给自己定义一个dict子类,然后在子类中实现__missing__方法。
4.3.1 defaultdict:处理找不到的键的一个选择
在用户创建defaultdict对象的时候,需要给构造方法提供一个可调用对象,这个可调用对象会在__getitem__碰到找不到的键的时候被调用,让__getitem__返回某种默认值。
示例index_default.py:
import sys
import re
import collections
WORD_RE=re.compile(r'\w+')
print(sys.argv[0])
index=collections.defaultdict(list) #1
with open(sys.argv[0],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[word].append(location) #2
for word in sorted(index,key=str.upper):
print(word,index[word])
1 把list构造方法作为default_factory来创造一个defaultdict。
2 如果index没有word的记录,那么default_factory会被调用,为查询不到的键创造一个值。这个值在这里是一个空列表,然后这个空列表会赋给index[word],继而被当作返回值返回,因此append(location)操作总能成功。
小心:defaultdict里的default_factory只会在__getitem__里调用,在其它方法里不会发挥作用。比如d[k]可以用,但是d.get(k)则会返回None
4.3.2 特殊方法__missing__
所有的映射类型在处理找不到的键的时候,都会牵扯到__missing__方法。我们可以写一个类继承了UserDict(为什么不继承dict的原因稍后再说),然后这个继承类提供了__missing__方法,那么在__getitem__碰到找不到的键的时候,python就会自动调用它,而不是抛出KeyError异常。
下面的StrKeyDict类,即使输入的键不是字符串,__missing__函数也可以把它处理为字符串。
import collections
class StrKeyDict(collections.UserDict):
def __missing__(self,key):
if isinstance(key, str):
raise KeyError(key)
return self[str(key)]
def __contains__(self, key):
return str(key) in self.data
def __setitem__(self, key, item):
self.data[str(key)]=item
In [17]: skd=StrKeyDict()
In [19]: skd['1']='a'
In [20]: skd[1]
Out[20]: 'a'
In [21]: skd['1']
Out[21]: 'a'
4.4 collection.Counter
这个映射类型会给键准备一个整数计数器。 每次更新一个键的时候都会增加这个计数器。 所以这个类型可以用来给可散列表对象计数, 或者是当成多重集来用——多重集合就是集合里的元素可以出现不止一次。 Counter 实现了 + 和 - 运算符用来合并记录, 还有像 most_common([n]) 这类很有用的方法。 most_common([n]) 会按照次序返回映射里最常见的 n 个键和它们的计数。
>>> ct = collections.Counter('abracadabra')
>>> ct
Counter({'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1})
>>> ct.update('aaaaazzz')
>>> ct
Counter({'a': 10, 'z': 3, 'b': 2, 'r': 2, 'c': 1, 'd': 1})
>>> ct.most_common(2)
[('a', 10), ('z', 3)]
4.5 总结
- 用setdefault处理找不到的键时,可以在查询的同时插入,并返回。
- 继承UserDict,实现__missing__函数,可以自定义处理查询失败的情况。
- 继承UserDict,不继承dict,可以避免很多麻烦。
要继承UserDict,不继承dict的原因请看下一篇博客
参考:流畅的python