4. 字典进阶

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 ,并返回 defaultdefault 默认为 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 总结

  1. 用setdefault处理找不到的键时,可以在查询的同时插入,并返回。
  2. 继承UserDict,实现__missing__函数,可以自定义处理查询失败的情况。
  3. 继承UserDict,不继承dict,可以避免很多麻烦。

要继承UserDict,不继承dict的原因请看下一篇博客
参考:流畅的python

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值