1.映射的弹性键查询,defaultdict-处理找不到的键的一个选择
在创建defaultdict对象的时候,就需要给它配置一个为找不到的键创造默认值的方法。
"""创建一个从单词到其出现情况的映射"""
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[word].append(location)
# 以字母顺序打印出结果
for word in sorted(index, key=str.upper):
print(word, index[word])
2.特殊方法__missing__
所有的映射类型在处理找不到的键的时候,都会牵扯到__missing__方法。如果有一个类继承了dict,然后这个继承类提供了__missing__方法,那么在__getitem__碰到找不到的键的时候,Python就会自动调用它,而不是抛出一个KeyError异常。
例:有时候会希望在查询的时候,映射类型里的键统统转换成str。
class StrKeyDict0(dict): # StrKeyDict0继承了dict
def __missing__(self, key):
if isinstance(key, str): # 如果找不到的键本身就是字符串,那就抛出KeyError异常
raise KeyError(key)
return self[str(key)] # 如果找不到的键不是字符串,那么就把它转换成字符串在进行查找
def get(self, key, default=None):
try:
"""
get方法把查找工作用self[key]的形式委托给__getitem__,这样在
宣布查找失败时,还能通过__missing__再给某个键一个机会
"""
return self[key]
except KeyError:
return default #如果抛出KeyError, 那么说明__missing__也失败了,返回default
def __contains__(self, key):
"""
先按照传入键的原本的值来查找(映射中可能含有非字符串的键),如果
没有找到,再用str方法把键转换成字符串在查找一次
"""
return key in self.keys() or str (key) in self.keys()
# tests for item retrieval using 'd[key]' notation::
d = StrKeyDict0([('2', 'two'), ('4', 'four')])
print(d['2'])
print(d[4])
# tests for item retrieval using 'd.get[key]' notation::
print(d.get('2'))
print(d.get(4))
print(d.get(1, 'N\A'))
# Tests for the 'in' operator::
print(2 in d)
print(1 in d)
运行结果:
3.字典的变种
collections.OrderedDict
这个类型再添加键的时候会保持顺序,因此键的迭代次序总是一致的。
collections.ChainMap
该类型可以容纳数个不用的映射对象,然后在进行键查找操作的时候,这些对象会被当做一个整体被逐个查找,直到键被找到为止。
collections.Counter
这个映射类型会给键准备一个整数计时器。
import collections
ct = collections.Counter('abracadabra')
print(ct)
ct.update('aaaaazzz')
print(ct)
print(ct.most_common(3))# 自己理解为最常出现的
运行结果:
collections.UserDict
把标准dict用纯Python又实现了一遍。
对__missing__方法中StrKeyDic类的优化:
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
4.不可变映射类型
types模块中引入了一个封装名叫MappingProxyType。如果给这个类一个映射,它会返回一个只读的映射视图。
from types import MappingProxyType
d = {
1:'A'}
d_proxy = MappingProxyType(d)
print(d_proxy)
print(d_proxy[1])
d_proxy[2] = 'x'
d[2] = 'B'
print(d_proxy)
运行结果:
不允许做任何修改
删除d_proxy[2] = 'x’后
运行结果:
对d的修改会反馈到d_proxy上。
5.集合论
集合的本质是许多唯一对象的聚集。因此,集合可以用于去重:
l = ['spam', 'spam', 'eggs', 'eggs']
print(set(l))
print(list(set(l)))
运行结果:
除了保证唯一性,集合还实现了很多基础的中缀运算符。a | b返回合集,a & b返回交集,a - b返回差集。
查找needles的元素在haysack里出现的次数。
needles_0 = ['s', 'a', 'b', 'd']
haystack_0 = ['s', 'c', 'd', 'a', 'b', 'f']
needles = set(needles_0)
haystack = set(haystack_0)
found = len(needles & haystack)
print(found)
运行结果:
如果不使用交集操作,就会变成如下代码:
needles_0 = ['s', 'a', 'b', 'd']
haystack_0 = ['s', 'c', 'd', 'a', 'b', 'f']
needles = set(needles_0)
haystack = set(haystack_0)
found = 0
for n in needles:
if n in haystack:
found += 1
print(found)
运行结果一样。
6.集合字面量
除空集之外,集合的字面量-{1},{1,2},等等——看起来它的数学形式一模一样。如果是空集,那么必须写成set()的形式。
s = {
1}
print(type(s))
print(s)
print(s.pop())
print(s)
字面量方法和构造方法的不同:
from dis import dis
print(dis('{1}'))
print(dis('set([1])'))
from unicodedata import name
print({
chr(i) for i in range(32, 256) if 'SIGN' in name(chr(i), '')})
运行结果:
8.dict的实现及其导致的结果
将同样的数据以不同的顺序添加到3个字典里:
DIAL_CODES = [
(86, 'China'),
(91, 'India'),
(1, 'United States'),
(62, 'Indonesia'),
(55, 'Brazil'),
(92, 'Pakistan'),
(880, 'Bangladesh'),
(234, 'Nigeria'),
(7, 'Russia'),
(81 , 'Japan'),
]
d1 = dict(DIAL_CODES)
print('d1:', d1.keys())
d2 = dict(sorted(DIAL_CODES))
print('d2:', d2.keys())
d3 = dict(sorted(DIAL_CODES, key=lambda x:x[1]))
print('d3:', d3.keys())