Python进阶篇之字典操作总结
一、与字典值有关的计算
问题
想对字典的值进行相关计算,例如找出字典里对应值最大(最小)的项。
解决方案一:
假设要从字典{'a':3,'b':2,'c':6}中找出值最小的项,可以这样做:
>>>d={'a':3,'b':2,'c':6} >>>min(zip(d.values(),d.keys())) (2,'b')
值得注意的是 d.values()获取字典的全部值,d.keys()获取字典的全部键,而且两个序列的顺序依然保持一一对应的关系。因此zip(d.values(),d.keys())实质上生成的是一个(value,key)的序列。min函数通过比较序列中的元组(value,key)找出其最小值。
解决方案二:
除了使用zip(d.values(), d.keys())外,还可以使用dict.items()方法和生成器推导式来生成(value,key)序列,从而传递给min函数进行比较:
>>>d={'a':3,'b':2,'c':6} >>>min((v,k)for(k,v)ind.items()) (2,'b')
这里min函数的参数(v,k)for(k,v)ind.items()其实是一个生成器推导式(和列表推导式一样,只是把列表推导式的[]改为(),而且其返回的一个生成器而非列表),由于生成器推导式做为min函数的参数,所以可以省略掉两边的括号(不做为参数时写法应该是((v,k)for(k,v)ind.items()))。
二、字典推导式
问题
想把一个元组列表转换成一个字典,例如把[('a',1),('b',2),('c',3)]转化为{'a':1,'b':2,'c':3}
解决方案
类似于列表推导式,字典推导式可以方便地从其他数据结构构造字典,例如:
>>>l=[('a',1),('b',2),('c',3)] >>>{k:vfork,vinl} {'c':3,'b':2,'a':1}
字典推导式的规则和列表推导式一样,只是把[]换成{}
三、寻找字典的交集
问题
假设有两个字典:
d1={'a':1,'b':2,'c':3,'d':4} d2={'b':2,'c':3,'d':3,'e':5}
要找出这两个字典中具有公共键的项,即要得到结果{'b':2,'c':3}
解决方案
我们知道一般通过d.items()方法来遍历字典,d.items()方法返回的对象是一个类集合对象,支持集合的基本运算,如取交集、并集等。
>>>dict(d1.items()&d2.items())#取交集 {'b':2,'c':3}
此外,d.keys()返回字典的键,也是一个类集合对象,如果我们只想找出两个字典中键相同的项,可以这样:
>>>{k:d1[k]forkind1.keys()&d2.keys()} {'b':2,'d':4,'c':3}
这里如果相同的键对应不同的值则去第一个字典中的值。推广开来,如果想排除掉字典中的某些键,可以这样:
>>>{k:d1[k]forkind1.keys()-{'c','d'}}#-号的含义是集合的差集操作 {'b':2,'a':1}
但有一点需要注意的是,d.values()返回字典的值,由于字典对应的值不一定唯一,所以d.values()一般无法构成一个集合,因此也就不支持一般的集合操作。
四、多个字典连接成一个字典
问题
有多个字典,例如:
d1={'a':1,'b':2,'c':3} d2={'c':4,'d':5,'e':6}
想将这多个字典连接为一个字典,或一次性对多个字典进行迭代操作。
解决方案
使用collections.ChainMap :
>>>fromcollectionsimportChainMap >>>chain_dict=ChainMap(d1,d2) >>>fork,vinchain_dict.items(): print(k,v) a1 e6 d5 c3 b2
ChainMap将传入的多个字典连接为一个字典,并返回一个ChainMap对象,这个对象的行为就像一个单一的字典,我们可以对其进行取值或者迭代等操作。注意到这里键c对应的值为3,如果传入ChainMap的字典含有相同的键,则对应的值为先传入的字典中的值。
此外,如果你只想单纯地迭代字典的键值对,可以结合使用items()和itertools.chain()方法:
>>>fromitertoolsimportchain >>>fork,vinchain(d1.items(),d2.items()): print(k,v) a1 c3 b2 e6 c4 d5
这里相同的键会被分别迭代出来。
五、保持字典有序
问题
想让字典中元素的迭代顺序和其加入字典的顺序保持一致
解决方案
通常来说,使用d.items()或者d.keys()、d.values()方法迭代出来的元素顺序是无法预料的。例如对字典d={'a':1,'b':2,'c':3}迭代:
>>>d=dict() >>>d['a']=1 >>>d['b']=2 >>>d['c']=3 >>>fork,vind.items(): print(k,v) a1 c3 b2
每一次运行结果都可能不同。如果想让元素迭代的顺序和创建字典时元素的顺序一致,就要使用collections.OrderedDict代替普通的dict:
>>>fromcollectionsimportOrderedDict >>>ordered_d=OrderedDict() >>>ordered_d['a']=1 >>>ordered_d['b']=2 >>>ordered_d['c']=3 >>>fork,vinordered_d.items(): print(k,v) a1 b2 c3
OrderedDict实际通过维护一个双向链表来记录元素添加的顺序,因此其耗费的内存大约为普通字典的两倍。所以在实际使用中需综合考虑各种因素来决定是否使用OrderedDict。
六、使字典的键映射多个值
问题
通常情况下字典的键只对应一个值。现在想让一个键对应多个值。
解决方案
为了使一个键对应多个值,首先需要把多个值放到一个容器中(例如列表或者集合等)。例如有这样一个列表:[('a',1),('a',2),('b',3),('b',4),('c',5)],我们要将其转换成一个字典,保持元素的键值对应关系,通常我们会写这样的代码:
>>>frompprintimportpprint >>>l=[('a',1),('a',2),('b',3),('b',4),('c',5)] >>>d={} >>>fork,vinl: ifkind: d[k].append(v) else: d[k]=[v] >>>pprint(d) {'a':[1,2],'b':[3,4],'c':[5]}
但是ifelse语句让代码显得有点冗余和不易读,Python的defaultdict改善上述代码。
>>>fromcollectionsimportdefaultdict >>>d=defaultdict(list) >>>fork,vinl: d[k].append(v) >>>pprint(d) defaultdict(<class'list'>,{'c':[5],'b':[3,4],'a':[1,2]})
ifelse的判语句没有了。
defaultdict是dict的一个子类。对dict来说,如果key不存在,则dict[key]取值操作会抛出KeyError异常,但是defaultdict则会返回一个传入defaultdict构造器的类的实例(例如一个列表)或者自定义的缺失值。因此在上例中,对于d[k].append(v),当k不存在时,则会先执行d[k]=[]并返回这个空列表,继而将v加入到列表中。
传入defualtdict构造器的值不一定要是一个类,也可以是一个可调用的函数,当相应的键不在defualtdict中时,其默认的值就为这个函数的返回值,例如:
>>>fromcollectionsimportdefaultdict >>>defzero_default(): return0 >>>d=defaultdict(zero_default) >>>d['a']=1 >>>d['a'] 1 >>>d['b'] 0 >>>d.keys() dict_keys(['b','a']) >>>
利用这样一个特性,我们可以构造无限深度的字典结构:
>>>fromcollectionsimportdefaultdict >>>importjson >>>tree=lambda:defaultdict(tree) >>>d=tree() >>>d['a']['b']=1 >>>print(json.dumps(d))#为了显示的格式更好看 {"a":{"b":1}}
这里当执行d['a']时,由于相应的键不存在,故返回一个defaultdict(tree),当再执行d['a']['b']=1时,将键b对应的值设为1。
总结
以上就是这篇文中的全部内容,希望本文的内容对大家学习或者使用python能有一定的帮助,如果有疑问大家可以留言交流。