字典是常用数据结构,存放的是有映射关系的数据。

字典中保存了两组数据,一组是键数据,称为key;另一组是值数据,称为value,这组数据可通过 key 来访问。字典中的 key 是非常关键
的数据,因此字典中的 key 不能重复,并且是不可变类型,也是可以 hash 的类型。

1、 创建字典
创建字典可用大括号(或花括号)来创建,也可使用 dict() 函数来创建。dict 是Python中的字典类型。

使用大括号创建字典时,就包含多个key-value 对,key 与 value 之间用英文冒号分隔,多个 key-value 之间用英文逗号分隔。示例如下:
scores = {'python': 80, 'java': 90}
print(scores)          # 输出:{'python': 80, 'java': 90}
empty_dict = {}     # 创建空字典
dict1 = {(10, 20): 'python', 30: 'java'}    # 使用元组作为字典的键创建字典

元组可以作为字典的 key ,但列表不能。因为列表是可变类型,字典的 key 必须是不可变类型,所以元组可以作为字典的键,列表不可以。使用 dict() 函数创建字典时,可传入多个列表或元组参数作为 key-value 对,这些列表或元组只能包含两个元素。示例如下:
persons = [('name', 'michael'), ('sex', 'male'), ('age', 22)]         # 创建一个元组列表
dict2 = dict(persons)                # 创建字典,元组的一对元素自动当成字典的键值对
print(dict2)                               # 输出:{'name': 'michael', 'sex': 'male', 'age': 22}

cars = [['BMW', 10], ['BETCH', 12], ['AUDI', 9.3]]
dict3 = dict(cars)                     # 使用列表中的一对数据同样可以创建字典

dict4 = dict()                           # 不传入任何参数,dict() 函数创建一个空字典,{}

# 通过为 dict 函数传入关键字参数创建字典,此时字典的 key 不能是表达式
dict5 = dict(name = 'michael', age = 22)

2、 字典的基本用法
字典包含多个 key-value 对,程序对字典的操作是基于 key 的。基本操作如下:
(1)、通过 key 访问 value。
(2)、通过 key 添加 key-value 对。
(3)、通过 key 删除 key-value 对。
(4)、通过 key 修改 key-value 对。
(5)、通过 key 判断指定 key-value 对是否存在。

字典将 key 放在方括号中访问 value 。列表和元组在方括号中放的是索引。要为 dict 添加 key-value 对,只需要为不存在的 key 赋值即可。删除字典中的 key-value 对,就使用 del 语句。对 dict 已经存在 key 赋新值时,就可以修改 dict 中的 key-value 对。示例如下:
persons = {'name': 'michael'}       # 创建一个字典,只有一个键值对
print(persons['name'])                 # 通过 key 访问字典的 value,输出是:michael
persons['sex'] = 'male'                # 对 key 不存在时,就在字典中添加键值对
persons['age'] = 22
def persons['sex']                       # 使用 del 语句删除字典中的键值对
print(persons)                            # 输出:{'name': 'michael', 'age': 22}
persons['name'] = 'jack'            # 当 key 在字典中存在时,就修改对应的值
print(persons)                           # 输出:{'name': 'jack', 'age': 22}

要判断某个 key 在字典是否存在,可使用 in 或 not in 运算符。要注意的是 in 或 not in 都是基于字典的 key 来判断的,不是基于 value 来判断的。示例如下:
print('sex' in persons)              # 判断字典中是否有 sex 这个键,输出:False
print('sex' not in persons)       # 判断字典是否不包含 sex 这个键,输出:True

字典的 key 是字典的索引,这个索引不一定是整数类型,字典的 key 可以是任意不可变类型。列表的索引是整数,而且是从0开始且连续的,列表不允许对不存在的索引赋值。字典的 key 可以是整数,不要求从0开始,key 可以不连续,对不存在的 key 赋值会添加一个键值对。

3、 字典的常用方法
字典由 dict 类代表,在命令行用 dir(dict) 可查看这个类包含的所有方法。执行的输出如下所示:
dir(dict)           # 过滤掉双下划线开头的方法后的输出如下:
['clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']

(1)、clear() 方法
清空字典内的所有 key-value 对,执行这个命令后,字典就是空字典。示例如下:
persons = {'name': 'michael', 'age': 25}
persons.clear()     # 清空字典

(2)、get() 方法
根据字典的 key 获取对应的 value 值,是方括号语法的增加版。使用方括号语法访问并不存在的 key 时,会引发 KeyError 错误;但是使用 get() 方法访问不存在的 key 时,默认会返回 None,还可以给 get() 方法指定默认返回值。示例如下:
persons = {'name': 'michael', 'age': 25}
print(persons['name'])                 # 使用方括号访问字典,输出:michael
print(persons['sex'])                    # 使用方括号访问字典中不存在的 key,引发KeyError错误
print(persons.get('name'))          # 使用 get 方法访问字典,输出:michael
print(persons.get('sex'))              # 使用 get 方法访问字典不存在的 key,默认输出:None
print(persons.get('sex', 'male'))   # 使用 get 方法访问字典不存在的 key,并指定默认值,输出:male

(3)、update() 方法
当字典调用 update 方法时,如果键在字典中存在,则该键在字典对应的旧值会被新值覆盖掉;如果键在字典不存时,就将键和值添加到字典中去。示例如下:
persons = {'name': 'michael', 'age': 25}
persons.update({'name': 'jack', 'sex': 'male'})     # 更新字典,注意参数是字典形式
print(persons)      # {'name': 'jack', 'age': 25, 'sex': 'male'}

(4)、items()、keys()、values() 方法
这三个方法分别获取字典中的所有 key-value 对、所有 key、所有 value。这三个方法依次返回的是 dict_items、dict_keys 和 dict_values 对象,这三个对象是都是可迭代对象,可用于循环语句中,还可通过 list() 函数把它们转换成列表。示例如下:
persons = {'name': 'jack', 'age': 25, 'sex': 'male'}
ims = persons.items()       # 获取字典中的所有键值对,得到一个 dict_items 对象
print(type(ims))                 # 输出:<class 'dict_items'>
print(list(ims))                   # 将 dict_items 对象转换为列表,输出:[('name', 'jack'), ('age', 25), ('sex', 'male')]
print(list(ims)[1])               # 访问键值对,输出:('age', 25)

kys = persons.keys()
print(type(kys))                # 输出:<class 'dict_keys'>
print(list(kys))                  # 输出:['name', 'age', 'sex']

vals = persons.values()
print(type(vals))             # 输出:<class 'dict_values'>
print(list(vals))               # 输出:['jack', 25, 'male']

在 Python 2.x 中,items()、keys()、values() 这三个方法返回的本来就是列表,不需要 list() 函数进行处理。

(5)、pop() 方法
该方法在字典获取指定键的值,并在字典中删除这个键值对。示例如下:
persons = {'name': 'jack', 'age': 25, 'sex': 'male'}
print(persons.pop('name'))      # 输出:'jack'
print(persons)                          # 输出:{'age': 25, 'sex': 'male'}

(6)、popitems() 方法
随机从字典中取出一个键值对。popitems() 方法实际上不是随机从字典取出一个键值对,总是取出的是字典中最后一个键值对。因为字典存储的键值对顺序是不确定的,因此使用 popitems() 方法感觉是随机取出的键值对,但实际是取出的最后一个键值对。就像列表的 pop 方法一样,默认总是取出列表的最后一个元素。

此外,popitems() 方法从字典中取出的键值对以元组的形式返回,因此可通过序列解包方式分别接收 key 和 value 两个值。示例如下:
persons = {'age': 25, 'sex': 'male'}
k, v = persons.popitem()
print(k, v)                 # 输出:sex male

(7)、setdefault() 方法
该方法根据 key 到字典中获取对应的 value 值。但是当这个 key 在字典中不存在时,该方法会为这个不存在的 key 设置一个默认的 value,然后再返回该 key 对应的 value。总之,setdefault() 方法总能指定 key 对应的 value,如果该 key-value 存在,则直接返回该 key 对应的 value;如果该 key-value 不存在,则先为该 key 设置默认的 value,然后再返回该 key 对应的 value。示例如下:
persons = {'age': 25}
print(persons.setdefault('name', 'michael'))        # 输出:michael
print(persons)          # 输出:{'age': 25, 'name': 'michael'}

(8)、 fromkeys() 方法
该方法有两个参数,第一参数个是可迭代序列,第二个是默认值。该方法根据第一个参数序列中元素为键,值由第二个参数决定来创建字典。第二个参数默认为 None。这个方法通常由 dict 类直接调用,不是字典类对象去调用。示例如下:
a_dict = dict.fromkeys('abc')       # 创建一个字典 a_dict,键有3个,值是默认的 None
print(a_dict)                                 # 输出:{'a': None, 'b': None, 'c': None}
b_dict = dict.fromkeys(('a', 'b', 'c'), True)   # 指定默认值是 True
print(b_dict)                                # 输出:{'a': True, 'b': True, 'c': True}

4、 使用字典格式化字符串
适用于字符串模板中含在大量变量的情况,在字符中模板中按 key 指定变量,通过字典为字符串模板中的 key 设置值。示例如下:
# 在字符串模板中使用 key
temp = '姓名是:%(name)s,年龄是:%(age)d,学号是:%(id)d'
persons = {'name': 'michael', 'age': 25, 'id': 20190808}
# 使用字典为字符串模板中的 key 传入值
print(temp % persons)         # 输出是:姓名是:michael,年龄是:25,学号是:20190808
persons2 = {'name': 'kate', 'age': 20, 'id': 20190810}
# 使用字典为字符串模板中的 key 传入值
print(temp % persons2)       # 输出是:姓名是:kate,年龄是:20,学号是:20190810

小结:
列表和元组代表元素有序、可通过索引访问元素的数据结构。两者区别是:列表是可变的,程序可添加、删除、替换列表元素;元组是不可变的,程序不能改变元组中的元素。

字典存放的是键值对(key-value),字典是无序的,通过字典的 key 访问对应的 value。

练习:
1、提示输入N个字符串,将其封装成元组,再计算并输出该元组乘以3的结果,接着再计算并输出该元组加上('python', 'linux')的结果
import random
n = random.randint(1, 10)
a_str = input("请输入%d个字符串,以空格分隔:" % n)
a_tuple = tuple(a_str)       # 封装成元组
a_tuple *= 3        # 元组乘以3的结果
print("元组乘以3的结果是:\n", a_tuple)
a_tuple += ('python', 'linux')
print("元组加上('python', 'linux')的结果是:\n", a_tuple)

2、将列表的 start 到 end 的所有元素复制到另一个 列表中
a_list = [1, 2, 3, 4, 5]        # 定义一个列表
b_list = a_list[1:4]            # 使用列表的切片法复制列表

3、输入一个整数n,生成一个长度为n的列表,将 n 个随机数放入列表中
import random
n = int(input("请输入一个整数:"))
# random.choice(seq) 方法在序列中随机选取一个,random.random() 方法随机得到一个小数
rand_num_list = [random.choice(range(1000)) for _ in range(n)]
# rand_num_list = random.sample(range(1000), n)     # 使用 random.sample() 方法同样可以做到
print(rand_num_list)

4、输入一个整数n,生成一个长度为n的列表,将 n 个随机的奇数放入列表中
import random
n = int(input("请输入一个整数:"))
odd_num_list = random.sample(range(1, 10000, 2), n)
print("%d个随机奇数是:" % n, odd_num_list)

5、输入一个整数n,生成一个长度为n的列表,将 n 个随机的大写字符放入列表中
import random
n = int(input("请输入一个整数:"))
uppercase_list = [chr(random.randint(65, 90)) for _ in range(n)]
print(sorted(uppercase_list))

6、输入N个字符串,将这N个字符串放入到列表中,并且去掉列表中重复的字符串后输出列表
str1 = input("请输入多个字符串,以空格作为分隔符:")
str_list = str1.split()
for s in str_list:
    if str_list.count(s) > 1:
        for _ in range(str_list.count(s) - 1):
            str_list.remove(s)
print(str_list)

7、输入任意多个整数,以空格区分,将这些整数转换成元组,输出该元组及其Hash值
num_str = input("请输入以空格为分隔符的任意多个整数:")
num_list = [int(s) for s in num_str.split()]
num_tuple = tuple(num_list)
print("整数元组是:", num_tuple)
print("整数元组的哈希值(Hash)是:", hash(num_tuple))

8、随机输入任意多个大写字母,使用 dict 统计每个字母输入的次数
s = input("随机输入任意多个大写字母:")
s_tuple = tuple(s)
s_dict = {}
for k in s_tuple:
    if k in s_dict.keys():
        s_dict[k] += 1
    else:
        s_dict[k] = 1
print(s_dict)