Python程序设计开发宝典
上QQ阅读APP看书,第一时间看更新

3.3 字典:反映对应关系的映射类型

字典(dict)是包含若干“键:值”元素的无序可变序列,字典中的每个元素包含用冒号分隔开的“键”和“值”两部分,表示一种映射或对应关系,也称为关联数组。定义字典时,每个元素的“键”和“值”之间用冒号分隔,不同元素之间用逗号分隔,所有的元素放在一对大括号“{}”中。

字典中元素的“键”可以是Python中任意不可变数据,例如整数、实数、复数、字符串、元组等类型等可哈希数据,但不能使用列表、集合、字典或其他可变类型作为字典的“键”。另外,字典中的“键”不允许重复,“值”是可以重复的。字典在内部维护的哈希表使得检索操作非常快。使用内置字典类型dict时不要太在乎元素的先后顺序,如果确实在乎元素顺序可以使用collections的OrderedDict类。值得一提的是,在Python 3.6中又对内置类型dict进行了优化,比Python 3.5.x大概能节约20%~25%的内存空间。

3.3.1 字典创建与删除

使用赋值运算符“=”将一个字典赋值给一个变量即可创建一个字典变量。

>>>aDict={'server': 'db.diveintopython3.org', 'database': 'mysql'}

也可以使用内置类dict以不同形式创建字典,在第2章曾经介绍过这种用法,实际上是调用了dict类的构造方法。

        >>>x=dict()                               #空字典
        >>>x={}                                   #空字典
        >>>keys=['a', 'b', 'c', 'd'] 
        >>>values=[1,2,3,4]
        >>>dictionary=dict(zip(keys, values))     #根据已有数据创建字典
        >>>d=dict(name='Dong', age=39)            #以关键参数的形式创建字典
        >>>aDict=dict.fromkeys(['name', 'age', 'sex'])
                                                  #以给定内容为“键”
                                                  #创建“值”为空的字典
        >>>aDict
        {'name': None, 'age': None, 'sex': None}

另外,Python还支持使用字典推导式快速生成符合特定条件的字典。

        >>>{i:str(i) for i in range(1,5)}
        {1: '1',2: '2',3: '3',4: '4'}
        >>>x=['A', 'B', 'C', 'D']
        >>>y=['a', 'b', 'b', 'd']
        >>>{i:j for i, j in zip(x, y)}
        {'A': 'a', 'C': 'b', 'B': 'b', 'D': 'd'}

与其他类型的对象一样,当不再需要时,可以直接删除字典,不再赘述。

3.3.2 字典元素的访问

字典中的每个元素表示一种映射关系或对应关系,根据提供的“键”作为下标可以访问对应的“值”,如果字典中不存在这个“键”会抛出异常。

        >>>aDict={'age': 39, 'score': [98,97], 'name': 'Dong', 'sex': 'male'}
        >>>aDict['age']                         #指定的“键”存在,返回对应的“值”
        39
        >>>aDict['address']                     #指定的“键”不存在,抛出异常
        KeyError: 'address'

为了避免程序运行时引发异常而导致崩溃,在使用下标的方式访问字典元素时,最好配合条件判断或者异常处理结构。

        >>>aDict={'age': 39, 'score': [98,97], 'name': 'Dong', 'sex': 'male'}
        >>>if'Age'in aDict:                     #首先判断字典中是否存在指定的“键”
            print(aDict['Age'])
        else:
            print('Not Exists.')

        Not Exists.
        >>>try:                                 #使用异常处理结构
            print(aDict['address'])
        except:
              print('Not Exists.')

        Not Exists.

字典对象提供了一个get()方法用来返回指定“键”对应的“值”,并且允许指定该键不存在时返回特定的“值”。例如:

        >>>aDict.get('age')                        #如果字典中存在该“键”则返回对应的“值”
        39
        >>>aDict.get('address', 'Not Exists.')     #指定的“键”不存在时返回指定的默认值
        'Not Exists.'
        >>>import string
        >>>import random
        >>>x=string.ascii_letters+string.digits
        >>>z=''.join((random.choice(x) for i in range(1000)))#生成1000个随机字符
        >>>d=dict()
        >>>for ch in z:                           #遍历字符串,统计频次
            d[ch]=d.get(ch,0)+1
        >>>for k, v in sorted(d.items()):         #查看统计结果
            print(k, ':', v)

字典对象的setdefault()方法用于返回指定“键”对应的“值”,如果字典中不存在该“键”,就添加一个新元素并设置该“键”对应的“值”(默认为None)。

        >>>aDict.setdefault('address', 'SDIBT')   #增加新元素
        'SDIBT'
        >>>aDict
        {'age': 39, 'score': [98,97], 'name': 'Dong', 'address': 'SDIBT', 'sex': 'male'}

对字典对象直接进行迭代或者遍历时默认是遍历字典的“键”,如果需要遍历字典的元素必须使用字典对象的items()方法明确说明,如果需要遍历字典的“值”则必须使用字典对象的values()方法明确说明。当使用len()、max()、min()、sum()、sorted()、enumerate()、map()、filter()等内置函数以及成员测试运算符in对字典对象进行操作时,也遵循同样的约定。

        >>>aDict={'age': 39, 'score': [98,97], 'name': 'Dong', 'sex': 'male'}
        >>>for item in aDict:                     #默认遍历字典的“键”
            print(item, end='')
        age score name sex
        >>>for item in aDict.items():             #明确指定遍历字典的元素
            print(item, end='')
        ('age',39) ('score', [98,97]) ('name', 'Dong') ('sex', 'male')
        >>>aDict.items()
        dict_items([('age',37), ('score', [98,97]), ('name', 'Dong'), ('sex', 'male')])
        >>>aDict.keys()
        dict_keys(['age', 'score', 'name', 'sex'])
        >>>aDict.values()
        dict_values([37, [98,97], 'Dong', 'male'])

3.3.3 元素的添加、修改与删除

当以指定“键”为下标为字典元素赋值时,有两种含义:①若该“键”存在,则表示修改该“键”对应的值;②若不存在,则表示添加一个新的“键:值”对,也就是添加一个新元素。

        >>>aDict={'age': 35, 'name': 'Dong', 'sex': 'male'}
        >>>aDict['age']=39                       #修改元素值
        >>>aDict['address']='SDIBT'              #添加新元素
        >>>aDict                                 #使用字典时并不需要太在意元素顺序
        {'age': 39, 'address': 'SDIBT', 'name': 'Dong', 'sex': 'male'}

使用字典对象的update()方法可以将另一个字典的“键:值”一次性全部添加到当前字典对象,如果两个字典中存在相同的“键”,则以另一个字典中的“值”为准对当前字典进行更新。

        >>>aDict={'age': 37, 'score': [98,97], 'name': 'Dong', 'sex': 'male'}
        >>>aDict.update({'a':97, 'age':39})        #修改’age’键的值,同时添加新元素’a':97
        >>>aDict
        {'score': [98,97], 'sex': 'male', 'a': 97, 'age': 39, 'name': 'Dong'}

字典对象的setdefault()方法也可以用来为字典添加新元素,3.3.2节中已经介绍了该方法的用法。如果需要删除字典中指定的元素,可以使用del命令。

        >>>del aDict['age']                      #删除字典元素
        >>>aDict
        {'score': [98,97], 'sex': 'male', 'a': 97, 'name': 'Dong'}

字典对象的pop()和popitem()方法可以弹出并删除指定的元素。

        >>>aDict={'age': 37, 'score': [98,97], 'name': 'Dong', 'sex': 'male'}
        >>>aDict.popitem()                       #弹出一个元素,对空字典会抛出异常
        ('age',37)                               #在Python 3.6.X中结果略有不同,是正常的
        >>>aDict.pop('sex')                      #弹出指定键对应的元素
        'male'
        >>>aDict
        {'score': [98,97], 'name': 'Dong'}

字典对象的clear()方法用于清空字典对象中的所有元素;copy()方法返回字典对象的浅复制,关于浅复制的介绍请参考3.1.3节的介绍。

3.3.4 标准库collections中与字典有关的类

Python标准库中提供了很多扩展功能,大幅度提高了开发效率。这里主要介绍collections中OrderedDict类、defaultdict类和Counter类,deque、namedtuple以及其他更多的类将在后面章节中进行介绍。

1.OrderedDict类

Python内置字典dict是无序的,如果需要一个可以记住元素插入顺序的字典,可以使用collections.OrderedDict。

        >>>import collections
        >>>x=collections.OrderedDict()           #有序字典
        >>>x['a']=3
        >>>x['b']=5
        >>>x['c']=8
        >>>x
        OrderedDict([('a',3), ('b',5), ('c',8)])

2.defaultdict类

前面3.3.2节中字母出现频次统计的问题,也可以使用collections模块的defaultdict类来实现。

        >>>import string
        >>>import random
        >>>x=string.ascii_letters+string.digits+string.punctuation
        >>>z=''.join([random.choice(x) for i in range(1000)])
        >>>from collections import defaultdict
        >>>frequences=defaultdict(int)           #所有值默认为0
        >>>frequences
        defaultdict(<class'int'>, {})
        >>>for item in z:
            frequences[item]+=1                  #修改每个字符的频次
        >>>frequences.items()

创建defaultdict对象时,传递的参数表示字典中值的类型,除了上面代码演示的int类型,还可以是任意合法的Python类型。

        >>>from collections import defaultdict
        >>>games=defaultdict(list)               #使用list作为值类型
        >>>games                                 #所有值默认为空列表
        defaultdict(<class'list'>, {})
        >>>games['name'].append('dong')          #可直接为字典games添加元素
        >>>games['name'].append('zhang')
        >>>games['score'].append(90)
        >>>games['score'].append(93)
        >>>games
        defaultdict(<class'list'>, {'score': [90,93], 'name': ['dong', 'zhang']})

3.Counter类

对于频次统计的问题,使用collections模块的Counter类可以更加快速地实现这个功能,并且能够提供更多的功能,例如,查找出现次数最多的元素。

        >>>from collections import Counter
        >>>frequences=Counter(z)                 #这里的z还是前面代码中的字符串对象
        >>>frequences.items()
        >>>frequences.most_common(1)             #返回出现次数最多的一个字符及其频率
        >>>frequences.most_common(3)             #返回出现次数最多的前3个字符及其频率