3.4
3.4
3.4 映射的弹性键查询
有时候,如果我们在字典中查询某个键,但同时,我们希望如果该键值对不存在时,也可以返回一个默认值给我们。那么在python 中,有两种途径可以帮我们达到这一效果。
- 使用defaultDict类型
- 定义一个dict的子类,然后在子类中实现__missing__方法。
第一种方法我们之前也有用到了,在我们实例化一个defaultDict的时候就需要为他创建一个找不到的键的默认值。
再具体点说:
我们创建一个dict
>>> my_dict = defaultdict(list)
然后我们查找my_dict[‘no’]时,no这个key肯定是不存在的。因此它会调用list()方法来新建一个列表,并且把这个新列表作为值,'no’作为他的键,放入my_dict中,紧接着返回这个列表的引用。
import sys
import re
import collections
WORD_RE = re.compile(r'\w+')
index = collections.defaultdict(list)
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)
for word in sorted(index, key=str.upper):
print(word, index[word])
append [(15, 25)]
argv [(8, 11), (9, 15)]
as [(9, 42)]
collections [(3, 8), (7, 9)]
column_no [(13, 13), (14, 34)]
...
import [(1, 1), (2, 1), (3, 1)]
in [(10, 23), (11, 19), (16, 10)]
line [(10, 18), (11, 39)]
line_no [(10, 9), (14, 25)]
...
w [(5, 25)]
with [(9, 1)]
word [(12, 13), (15, 19), (16, 5), (17, 11), (17, 23)]
WORD_RE [(5, 1), (11, 22)]
把list构造方法作为default_factory来创建一个defaultdict。
如果index中并没有word记录,那么default——factory就会被调用,为查询不到的键创造一个value——就是一个空的列表。并且该空列表被赋值给了index[word]并且返回。
当然,defaultdict里的default_factory只会在__getitem__中被调用。上个my_dict的例子中,my_dict[‘no’]就会调用default_factory创造默认值。如果使用的是my_dict.get(“no”)则会返回none。
KaTeX parse error: Expected group after '_' at position 5: 特殊方法_̲_ missing__
所有的映射类型如果找不到对应的键,则会用到__missing__方法。
而这个方法只会被__getitem__调用。对get的方式没有影响。
class StrKeyDict0(dict):
def __missing__(self, key):
if isinstance(key, str):
raise KeyError(key)
return self[str(key)]
def get(self, k, default=None):
try:
return self[k]
except KeyError:
return default
def __contains__(self, key):
return key in self.keys() or str(key) in self.keys()
d = StrKeyDict0([('2', 'two'), ('4', 'four')])
print(d[2])
print(d['4'])
print(d.get("1"))
print(d[1])
two
four
None
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "/Applications/PyCharm.app/Contents/helpers/pydev/_pydev_bundle/pydev_umd.py", line 197, in runfile
pydev_imports.execfile(filename, global_vars, local_vars) # execute the script
File "/Applications/PyCharm.app/Contents/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "/Users/xx/PycharmProjects/test/a.py", line 21, in <module>
print(d[1])
File "/Users/xx/PycharmProjects/test/a.py", line 5, in __missing__
return self[str(key)]
File "/Users/xx/PycharmProjects/test/a.py", line 4, in __missing__
raise KeyError(key)
KeyError: '1'
该类继承了Dict类,找不到键2时,会用字符串‘2’再找一次,于是d[2]时候返回了two。但是用d[1]的时候,用字符串同样找不到,于是报了KeyError。
我们在创建它的时候,并没有传入的值的类型进行限制,这样也使得我们在查找时十分的友好。
3.5 3.5 3.5 字典的变种
这一小节总结一下标准库collections中的映射类型。
- collections.OrderedDict
key有顺序的dict,类似Java中的TreeMap
- collections.ChainMap(*maps)
将多个dict当成一个dict,按顺序搜索key,只要搜索到key,结果返回成功。如果没有maps被指定,就提供一个默认的空字典,这样一个新链至少有一个映射。
- collections.Counter
持有key的计数,most_common(n)方法会按次序返回映射中最常见的n个键和计数
实例如下:
>>> import collections
>>> ct = collections.Counter('abcdeeafffw')
>>> ct
Counter({'f': 3, 'a': 2, 'e': 2, 'b': 1, 'c': 1, 'd': 1, 'w': 1})
>>> ct.update('fwww')
>>> ct
Counter({'f': 4, 'w': 4, 'a': 2, 'e': 2, 'b': 1, 'c': 1, 'd': 1})
>>> ct.most_common(2)
[('f', 4), ('w', 4)]
- collections.UserDict([initialdata])
纯Python实现的标准dict
一般来说userDict被用来供其他几个直接使用。这个实例的内容保存为一个正常字典,可以通过UserDict实例的data属性存取。如果提供了initialdata值,data就被初始化为它的内容。
如果我们想要自定义一个类,最好是继承UserDict,这样的话我们可以避免一些不必要的重写。
那上一个StrKeyDict()的例子来说:
class StrKeyDict0(dict):
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
它的data属性实际上是它最终存储数据的地方。
UserDict继承的是MutableMapping类
dict对象的最原始的接口描述是 collections 模块中的 Mapping 和 MutableMapping 这两个虚拟类,如下所示:
因此在后一个实例中,就不用再改写__get__方法了。