一、Python 字典
Python 的字典 dictionary 是一种灵活的数据结构类型,字典的每个键值对(key=>value)用冒号(:)分割,每个对之间用逗号(,)分割。 Python 字典里的键必须独一无二,但值则不必的。字典的值可以取任何数据类型,但必须是不可变的(unhashable),如字符串、元组或数值,用列表是不行的。
二、字典的创建
字典的创建主要有 2 种方法:
如下所示,分别使用 2 种方法创建了字典 d1 和字典 d2:
>> > d1 = { 'key3' : 4 , 'key2' : 5 , 'key1' : 4 }
>> > print ( d1)
{ 'key3' : 4 , 'key2' : 5 , 'key1' : 4 , }
>> > d2 = dict ( )
>> > d2[ 'key5' ] = 1
>> > print ( d2)
{ 'key5' : 1 }
三、访问字典的键与值
可以直接根据 dict[‘键名’] 获取一个值,也可以通过 dict.keys() 获取所有的键或者 dict.values() 获取所有的值。但需要注意的是 dict.keys() 或 dict.values() 返回的并非列表,需要使用 list 方法才能将所有的键和值转化为列表形式:
>> > d1 = { 'key3' : 4 , 'key2' : 5 , 'key1' : 4 }
>> > print ( d1[ 'key3' ] )
4
>> > print ( d1. keys( ) )
dict_keys( [ 'key3' , 'key2' , 'key1' ] )
>> > print ( list ( d1. keys( ) ) )
[ 'key3' , 'key2' , 'key1' ]
>> > print ( list ( d1. values( ) ) )
[ 4 , 5 , 4 ]
当尝试用一个不存在的键获取值时会出现 KeyError,最好是在获取值前先使用 if key in dict 来判断一个键是否已经存在。
>> > print ( d1[ 'key6' ] )
Traceback ( most recent call last) :
File "<input>" , line 1 , in < module>
KeyError: 'key6'
>> > print ( 'key6' in d1)
False
四、遍历字典
使用 for key, value in dict.items()可以遍历整个字典,如果只喜欢遍历键名或值,可以只使用 for key in dict.keys() 或 for value in dict.values()。
for key, value in d1. items( ) :
print ( "{}->{}" . format ( key, value) )
for key in d1. keys( ) :
print ( key)
for value in d1. values( ) :
print ( value)
五、字典数据的添加
字典与列表一样都是可变类型的数据,所以可以实现数据的添加和修改。 实现的方法主要有两种:
dict 自带的 update 方法非常有用,不仅可以插入新的键值对,还是实现两个字典合并的首选方法:
>> > print ( d1)
{ 'key3' : 4 , 'key2' : 5 , 'key1' : 4 }
>> > d1. update( { 'key4' : 2 } )
>> > print ( d1)
{ 'key3' : 4 , 'key2' : 5 , 'key1' : 4 , 'key4' : 2 }
>> > d1[ 'key5' ] = 1
>> > print ( d1)
{ 'key3' : 4 , 'key2' : 5 , 'key1' : 4 , 'key4' : 2 , 'key5' : 1 }
注意: 当创建的键名已存在时,dict[‘键名’]只会更新现有值,而不会创建新的键值对。
六、删除字典元素
利用 del dict[‘key’] 可以删除一个键值对,利用 del dict 可以删除整个字典, 使用 dict.clear() 方法可以清空一个字典:
>> > print ( d1)
{ 'key3' : 4 , 'key2' : 5 , 'key1' : 4 , 'key4' : 2 , 'key5' : 1 }
>> > del d1[ 'key5' ]
>> > print ( d1)
{ 'key3' : 4 , 'key2' : 5 , 'key1' : 4 , 'key4' : 2 }
>> > d1. clear( )
>> > print ( d1)
{ }
>> > del d1
>> > print ( d1)
Traceback ( most recent call last) :
File "<input>" , line 1 , in < module>
NameError: name 'd1' is not defined
七、利用 zip 方法由两个列表创建字典
zip()函数来可以把 2 个或多个列表合并,并创建一个元组对的列表,使用 dict 方法可以将其变成字典。 元组对的数量以合并列表的最短长度为准,Python 3 中 zip 方法合并列表后生成的是 zip 对象,必需使用 dict 方法才能将其变成字典:
>> > l1 = [ 1 , 2 , 3 ]
>> > l2 = [ 'x' , 'y' , 'z' ]
>> > l3 = [ 'x' , 'y' ]
>> > zip ( l1, l2)
< zip object at 0x031D6828 >
>> > print ( list ( zip ( l1, l2) ) )
[ ( 1 , 'x' ) , ( 2 , 'y' ) , ( 3 , 'z' ) ]
>> > print ( list ( zip ( l1, l3) ) )
[ ( 1 , 'x' ) , ( 2 , 'y' ) ]
>> > print ( dict ( zip ( l1, l3) ) )
{ 1 : 'x' , 2 : 'y' }
实际上 zip 方法支持所有可迭代对象(字符串、列表、元祖、字典),而不仅仅是列表。利用这个特性,可以很容易创建各种字典,包括很复杂的字典。如下所示,注意 zip 对象支持直接遍历,不需要先转成 list 或 dict:
>> > l1 = [ 1 , 2 , 3 ]
>> > str1 = "abc"
>> > print ( dict ( zip ( l1, str1) ) )
{ 1 : 'a' , 2 : 'b' , 3 : 'c' }
>> > name = [ "John" , "Jim" , "Lucy" ]
>> > year = [ 1983 , 1985 , 1995 ]
>> > birth_year = dict ( zip ( name, year) )
>> > print ( birth_year)
{ 'John' : 1983 , 'Jim' : 1985 , 'Lucy' : 1995 }
>> > for name, year in zip ( name, year) :
print ( "{} - {}" . format ( name, year) )
John - 1983
Jim - 1985
Lucy - 1995
八、利用 zip 方法实现键值反转
上文提到 zip 方法支持所有可迭代对象,也自然支持 dict.values() 和 dict.keys(),利用这个特性可以快速实现一个字典的键值反转,如下所示:
>> > d1 = { 'key3' : 4 , 'key2' : 5 , 'key1' : 4 , 'key4' : 2 , }
>> > d2 = dict ( zip ( d1. values( ) , d1. keys( ) ) )
>> > print ( d2)
{ 4 : 'key1' , 5 : 'key2' , 2 : 'key4' }
为什么使用 zip 方法后字典少了一个键? 还记得之前提到过键名必需唯一吗? 可见 zip 方法对字典数据进行反转必需考虑值相同而导致数据丢失的问题。
九、单个字典根据键名或值排序
使用 sorted 方法可以对单个字典的键名进行正向或逆向排序(reverse=True),得到是一个排过序的键名列表。然后通过使用 for key in sorted(d)遍历排过序的键从而得到排过序的值。
>> > d1 = { 'key3' : 4 , 'key2' : 5 , 'key1' : 4 , 'key4' : 2 , }
>> > d3 = sorted ( d1)
>> > print ( d3)
[ 'key1' , 'key2' , 'key3' , 'key4' ]
>> > print ( sorted ( d1, reverse= True ) )
[ 'key4' , 'key3' , 'key2' , 'key1' ]
>> > for key in sorted ( d1) :
print ( d1[ key] )
4
5
4
2
如果希望根据值来排序,一般有 2 种方法:
使用匿名函数 lambda,此方法通常是一种更好的方法。 使用 zip 方法反转字典:
>> > d1 = { 'key3' : 4 , 'key2' : 5 , 'key1' : 4 , 'key4' : 2 , }
>> > d2 = dict ( zip ( d1. values( ) , d1. keys( ) ) )
>> > print ( list ( d2. keys( ) ) )
[ 4 , 5 , 2 ]
>> > for key in sorted ( d2) :
print ( d2[ key] )
key4
key1
key2
由于使用 zip 方法导致了数值的缺失,一个更好的方法是使用 lambda 函数指定 key 来排序。 使用 lambda 匿名函数,该方法设置排序的 key 为 d1[k],即每个键值对的值来排序:
>> > d1 = { 'key3' : 4 , 'key2' : 5 , 'key1' : 4 , 'key4' : 2 , }
>> > d2 = sorted ( d1, key= lambda k: d1[ k] )
>> > print ( d2)
[ 'key4' , 'key3' , 'key1' , 'key2' ]
>> > for key in d2:
print ( d1[ key] )
2
4
4
5
十、字典的运算
假设有如下两个字典,能实现直接相加或相减吗? 答案是不能的。Python 3并不支持字典的直接相加或相减,如果要合并两个字典,需要使用 dict.update() 方法:
>> > d1 = { 'key1' : 1 , 'key2' : 7 }
>> > d2 = { 'key1' : 3 , 'key3' : 4 }
虽然 Python 不支持加减运算,但支持针对 keys() 和 values() 类似集合的运算, 可以用来求同求异,代码如下所示:
>> > d1. keys( ) & d2. keys( )
{ 'key1' }
>> > d1. keys( ) - d2. keys( )
{ 'key2' }
十一、提取字典子集
假设有如下一个字典(姓名->分数),需要提取分数大于 60 的键值对组成一个新的字典子集,那么应该怎么办呢?
d = { 'John' : 50 , 'Mary' : 65 , 'Kitty' : 90 , 'Tom' : 100 }
>> > d = { 'John' : 50 , 'Mary' : 65 , 'Kitty' : 90 , 'Tom' : 100 }
>> > sub_d = { key: value for key, value in d. items( ) if value >= 60 }
>> > print ( sub_d)
{ 'Mary' : 65 , 'Kitty' : 90 , 'Tom' : 100 }
十二、字典列表的排序
实际应用中更常见的是对字典列表排序,而不是对当个字典的键或值进行排序。假设有一个字典列表,想根据某个或某几个字典字段来排序这个列表,这时需要使用 operator 模块的 itemgetter 函数,可以非常容易的排序这样的数据结构。假设从数据库中检索出来网站会员信息列表,并且以下列的数据结构返回:
rows = [
{ 'fname' : 'Brian' , 'lname' : 'Jones' , 'uid' : 1003 } ,
{ 'fname' : 'David' , 'lname' : 'Beazley' , 'uid' : 1002 } ,
{ 'fname' : 'John' , 'lname' : 'Cleese' , 'uid' : 1001 } ,
{ 'fname' : 'Big' , 'lname' : 'Jones' , 'uid' : 1004 } ]
根据任意的字典字段来排序输入结果行是很容易实现的,代码示例:
from operator import itemgetter
rows_by_fname = sorted ( rows, key= itemgetter( 'fname' ) )
print ( rows_by_fname)
代码的输出如下,注意该方法返回的直接排过序的完整字典,而不是排过序的键名列表:
[ { 'fname' : 'Big' , 'uid' : 1004 , 'lname' : 'Jones' } ,
{ 'fname' : 'Brian' , 'uid' : 1003 , 'lname' : 'Jones' } ,
{ 'fname' : 'David' , 'uid' : 1002 , 'lname' : 'Beazley' } ,
{ 'fname' : 'John' , 'uid' : 1001 , 'lname' : 'Cleese' } ]
itemgetter() 函数也支持多个 keys,如下:
rows_by_lfname = sorted ( rows, key= itemgetter( 'lname' , 'fname' ) )
print ( rows_by_lfname)
[ { 'fname' : 'David' , 'uid' : 1002 , 'lname' : 'Beazley' } ,
{ 'fname' : 'John' , 'uid' : 1001 , 'lname' : 'Cleese' } ,
{ 'fname' : 'Big' , 'uid' : 1004 , 'lname' : 'Jones' } ,
{ 'fname' : 'Brian' , 'uid' : 1003 , 'lname' : 'Jones' } ]
十三、字典列表的去重
假设有如下一个字典列表,需要实现字典值的去重,即提取所有独特的值组成一个列表,那么一个最好的方式就是使用列表表达式:
>> > d1 = [ { 'key1' : 2 } , { 'key1' : 3 } , { 'key2' : 2 } , { 'key3' : 4 } ]
>> > u_value = set ( val for dict in d1 for val in dict . values( ) )
>> > print ( list ( u_value) )
[ 2 , 3 , 4 ]
同样,可以使用列表表达式提取所有独特的键名组成一个列表:
>> > d1 = [ { 'key1' : 2 } , { 'key1' : 3 } , { 'key2' : 2 } , { 'key3' : 4 } ]
>> > u_key = set ( key for dict in d1 for key in dict . keys( ) )
>> > print ( list ( u_key) )
[ 'key2' , 'key3' , 'key1' ]