tongsiying

阅读|运动|自律

0%

第12篇:高级数据类型之字典

基础数据类型:
  • int,整数类型(整形)

  • float,浮点类型(浮点型)

  • complex,复数类型

  • bool,布尔类型

  • str,字符串类型

高级数据类型

  • list,列表类型

  • tuple,元组类型

  • set,集合类型

  • dict,字典类型

学习目标:

掌握字典相关知识。

迄今为止,我们已经为大家介绍了Python中的三种容器型数据类型,但是这些数据类型还不足以帮助我们解决所有的问题。例如,我们要保存一个人的信息,包括姓名、年龄、体重、单位地址、家庭住址、本人手机号、紧急联系人手机号等信息,你会发现我们之前学过的列表、元组和集合都不是最理想的选择。

1
2
3
person1 = ['王小二', 22, 60, '榆次区新建北路1258号', '河北石家庄', '139354028795', '13800998877']
person2 = ['王小二', 22, 60, '榆次区新建北路1258号', '河北石家庄', '139354028795', '13800998877']
person3 = ['王小二', 22, 60, '榆次区新建北路1258号', '河北石家庄', '139354028795', '13800998877']

集合肯定是最不合适的,因为集合有去重特性,如果一个人的年龄和体重相同,那么集合中就会少一项信息;同理,如果这个人的家庭住址和单位地址是相同的,那么集合中又会少一项信息。另一方面,虽然列表和元组可以把一个人的所有信息都保存下来,但是当你想要获取这个人的手机号时,你得先知道他的手机号是列表或元组中的第6个还是第7个元素;当你想获取一个人的家庭住址时,你还得知道家庭住址是列表或元组中的第几项。总之,在遇到上述的场景时,列表、元组、字典都不是最合适的选择,我们还需字典(dictionary)类型,这种数据类型最适合把相关联的信息组装到一起,并且可以帮助我们解决程序中为真实事物建模的问题。

说到字典这个词,大家一定不陌生,读小学的时候每个人基本上都有一本《新华字典》,如下图所示。

Python程序中的字典跟现实生活中的字典很像,它以键值对(键和值的组合)的方式把数据组织到一起,我们可以通过键找到与之对应的值并进行操作。就像《新华字典》中,每个字(键)都有与它对应的解释(值)一样,每个字和它的解释合在一起就是字典中的一个条目,而字典中通常包含了很多个这样的条目。

一、字典的定义

通过对比列表、元组和集合,我们来看一下字典的定义:

  • 列表(list),是一个有序可变的容器,在里面可以存放多个不同类型数据元素可重复的元素。

  • 元组(tuple),是一个有序不可变的容器,在里面可以存放多个不同类型数据元素可重复的元素。

  • 集合(set),是一个 无序可变的容器,在里面可以存放多个不同类型数据元素不可重复的元素。

  • 字典(dict),是一个 有序可变、元素只能是键值对的容器,在里面可以存放多个不同类型键不可重复的元素。

  • 字典的特点

    • 元素必须键值对
    • 键不重复,重复则会被覆盖
    • 有序(在Python3.6+字典就是有序了,之前的字典都是无序)
    • 数据元素可以改变
  • 字典中对键值的要求:

    • 键:必须可哈希。 目前为止学到的可哈希的类型:int/bool/str/tuple;不可哈希的类型:list/set/dict。
    • 值:任意类型。
  • 注意点

    • 字典的键和集合的元素在遇到 布尔值 和 1、0 时,需注意重复的情况。

二、字典的创建

在Python中创建字典可以使用{}字面量语法,这一点跟上一节讲的集合是一样的。但是字典的{}中的元素是以键值对的形式存在的,每个元素由:分隔的两个值构成,:前面是键,:后面是值,代码如下所示。

1
2
3
4
5
6
7
8
9
10
xinhua = {
'峘': '与大山相连时显得较高的小山', '洹': '洹河,水名,又叫安阳河。在河南省安阳',
'桓': '鼓捣立在驿站、官署等建筑物旁作为标志的木柱,后称华表', '幻': '空虚的,不真实的'
}
print(xinhua)
person = {
'name': '王小二', 'age': 22, 'weight': 60, 'office': '榆次区新建北路1258号',
'home': '河北石家庄', 'tel': '139354028795', 'econtact': '13800998877'
}
print(person)

通过上面的代码,相信大家已经看出来了,用字典来保存一个人的信息远远优于使用列表或元组,因为我们可以用:前面的键来表示条目的含义,而:后面就是这个条目所对应的值。

当然,如果愿意,我们也可以使用内置函数dict或者是字典的生成式语法来创建字典,代码如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 创建空字典
dict1 = {}
dict2 = dict()

# dict函数(构造器)中的每一组参数就是字典中的一组键值对
person = dict(name='王小二', age=22, weight=60, home='榆次区新建北路')
print(person) # {'name': '王小二', 'age': 22, 'weight': 60, 'home': '榆次区新建北路'}

# 可以通过Python内置函数zip压缩两个序列并创建字典
items1 = dict(zip('ABCDE', '12345'))
print(items1) # {'A': '1', 'B': '2', 'C': '3', 'D': '4', 'E': '5'}
items2 = dict(zip('ABCDE', range(1, 10)))
print(items2) # {'A': 1, 'B': 2, 'C': 3, 'D': 4, 'E': 5}

# 用字典生成式语法创建字典
items3 = {x: x ** 3 for x in range(1, 6)}
print(items3) # {1: 1, 2: 8, 3: 27, 4: 64, 5: 125}

想知道字典中一共有多少组键值对,仍然是使用len函数;如果想对字典进行遍历,可以用for循环,但是需要注意,for循环只是对字典的键进行了遍历,不过没关系,在讲完字典的运算后,我们可以通过字典的键获取到和这个键对应的值。

1
2
3
4
person = { 'name': '王小二', 'age': 22, 'weight': 60, 'office': '榆次区新建北路1258号'}
print(len(person)) # 4
for key in person:
print(key)

三、字典的方法

字典类型的方法基本上都跟字典的键值对操作相关,字典中常见的方法如下:

  • 添加
    • setdefault(key, value):无此键则添加,有此键则不变
    • dict[key] = value:无此键则添加,有此键则更新
  • 删除
    • pop(key):删除指定键对应的键值对,返回key对应的值
    • popitem(),按顺序删除一个键值对,先进先出,返回这个键值对对应的二元组(key, value)
    • del dict[key]:删除指定键对应的键值对,无返回值
  • 更新
    • dict[key] = value:若key存在则修改为value
    • update(dict2):将dic2中的所有键值对覆盖添加(相同的键覆盖,不同的键值对添加)到dic中,dic2不变
  • 查询
    • get(key, default=None):如果指定的键key存在,则返回对应的值,否则返回default,default默认为空。
    • keys():获取字典所有的键,返回的是高仿列表,这个高仿的列表可以被循环显示,不能通过下标取值。
    • values():获取字典所有的值,返回的是高仿列表,这个高仿的列表可以被循环显示, 不能通过下标取值。
    • items():获取字典所有的键值对,返回的是高仿列表,列表中的元素为(key, value)形式的二元组,这个高仿的列表可以被循环显示,不能通过下标取值。
  • 清空
    • clear(),清空字典

可以通过下面的例子来了解这些方法的使用。例如,我们要用一个字典来保存学生的信息,我们可以使用学生的学号作为字典中的键,通过学号做索引运算就可以得到对应的学生;我们可以把字典中键对应的值也做成一个字典,这样就可以用多组键值对分别存储学生的姓名、性别、年龄、籍贯等信息,代码如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 字典中的值又是一个字典(嵌套的字典)
students = {
1001: {'name': '狄仁杰', 'sex': True, 'age': 22, 'place': '山西大同'},
1002: {'name': '白元芳', 'sex': True, 'age': 23, 'place': '河北保定'},
1003: {'name': '武则天', 'sex': False, 'age': 20, 'place': '四川广元'}
}

# 使用get方法通过键获取对应的值,如果取不到不会引发KeyError异常而是返回None或设定的默认值
print(students.get(1002)) # {'name': '白元芳', 'sex': True, 'age': 23, 'place': '河北保定'}
print(students.get(1005)) # None
print(students.get(1005, {'name': '无名氏'})) # {'name': '无名氏'}

# 获取字典中所有的键
print(students.keys()) # dict_keys([1001, 1002, 1003])
# 获取字典中所有的值
print(students.values()) # dict_values([{...}, {...}, {...}])
# 获取字典中所有的键值对
print(students.items()) # dict_items([(1001, {...}), (1002, {....}), (1003, {...})])
# 对字典中所有的键值对进行循环遍历
for key, value in students.items():
print(key, '--->', value)

# 使用pop方法通过键删除对应的键值对并返回该值
stu1 = students.pop(1002)
print(stu1) # {'name': '白元芳', 'sex': True, 'age': 23, 'place': '河北保定'}
print(len(students)) # 2
# stu2 = students.pop(1005) # KeyError: 1005
stu2 = students.pop(1005, {})
print(stu2) # {}

# 使用popitem方法删除字典中最后一组键值对并返回对应的二元组
# 如果字典中没有元素,调用该方法将引发KeyError异常
key, value = students.popitem()
print(key, value) # 1003 {'name': '武则天', 'sex': False, 'age': 20, 'place': '四川广元'}

# setdefault可以更新字典中的键对应的值或向字典中存入新的键值对
# setdefault方法的第一个参数是键,第二个参数是键对应的值
# 如果这个键在字典中存在,更新这个键之后会返回原来与这个键对应的值
# 如果这个键在字典中不存在,方法将返回第二个参数的值,默认为None
result = students.setdefault(1005, {'name': '方启鹤', 'sex': True})
print(result) # {'name': '方启鹤', 'sex': True}
print(students) # {1001: {...}, 1005: {...}}

# 使用update更新字典元素,相同的键会用新值覆盖掉旧值,不同的键会添加到字典中
others = {
1005: {'name': '乔峰', 'sex': True, 'age': 32, 'place': '北京大兴'},
1010: {'name': '王语嫣', 'sex': False, 'age': 19},
1008: {'name': '钟灵', 'sex': False}
}
students.update(others)
print(students) # {1001: {...}, 1005: {...}, 1010: {...}, 1008: {...}}

跟列表一样,从字典中删除元素也可以使用del关键字,在删除元素的时候如果指定的键索引不到对应的值,一样会引发KeyError异常,具体的做法如下所示。

1
2
3
person = {'name': '王大锤', 'age': 25, 'sex': True}
del person['age']
print(person) # {'name': '王大锤', 'sex': True}

四、字典的运算

对于字典类型来说,和集合类似,支持成员运算、索引运算,前者可以判定指定的键在不在字典中,后者可以通过键获取对应的值或者向字典中加入新的键值对。除此之外,python3.9还引入了并集运算。值得注意的是,字典的索引不同于列表的索引,列表中的元素因为有属于自己有序号,所以列表的索引是一个整数;字典中因为保存的是键值对,所以字典的索引是键值对中的键,通过索引操作可以修改原来的值或者向字典中存入新的键值对。需要特别提醒大家注意的是,字典中的键必须是不可变类型,例如整数(int)、浮点数(float)、字符串(str)、元组(tuple)等类型的值;显然,列表(list)和集合(set)是不能作为字典中的键的,当然字典类型本身也不能再作为字典中的键,因为字典也是可变类型,但是字典可以作为字典中的值。关于可变类型不能作为字典中的键的原因,我们在后面的课程中再为大家详细说明。这里,我们先看看下面的代码,了解一下字典的常见运算。

字典的常见运算如下:

  • 成员运算
    • in:判断指定键是否在字典中存在,若存在返回True,否则返回False
    • not in:判断指定键是否在字典中存在,若存在返回True,否则返回False
  • 索引运算
    • dict[key]:获取指定key对应的value,和字典的get方法不同的是,若key不存在,会引发KeyError异常
    • dict[key] = value:若key存在,则将key对应的值更新为value,否则添加键值对key和value
  • 长度
    • len():获取字典中元素的个数
  • 并集
    • 字典A | 字典B:返回两个字典的并集,结果中包含A和B所有的键,若字典A中不存在则添加,若存在则更新。

使用示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
person = {'name': '张三', 'age': 18, 'weight': 60, 'office': '榆次区新建北路98号'}

# 成员运算 检查name和tel两个键在不在person字典中
print('name' in person, 'tel' in person) # True False

# 索引运算 获取 通过age修将person字典中对应的值修改为25
if 'age' in person:
person['age'] = 25

# 索引运算 添加和更新 通过索引操作向person字典中存入新的键值对
person['tel'] = '13122334455'
person['signature'] = '你的男朋友是一个盖世英雄,他会踏着五彩祥云去娶你'
print('name' in person, 'tel' in person) # True True

# 长度 检查person字典中键值对的数量
print(len(person)) # 6

# 并集 python3.9引入
# person2 = {'home': '河北石家庄', 'age': 23, 'tel': '139354028795'}
# person3 = person | person2
# print(person3)

需要注意,在通过索引运算获取字典中的值时,如指定的键没有在字典中,将会引发KeyError异常。

五、字典的循环遍历

由于字典也属于是容器,内部可以包含多个键值对,可以通过循环对其中的:键、值、键值进行循环;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 默认循环key
info = {"age":12, "status":True,"name":"张亚飞"}
for item in info:
print(item) # 所有键

# 循环所有key
info = {"age":12, "status":True,"name":"张亚飞"}
for item in info.key():
print(item)

# 循环所有value
info = {"age":12, "status":True,"name":"张亚飞"}
for item in info.values():
print(item)

# 循环所有(key, value)
info = {"age":12, "status":True,"name":"张亚飞"}
for key, value in info.items():
print(key,value)

六、字典的类型转换

只有列表里面嵌套二元组的数据格式可以转化为字典,语法为:dict(data),字典转列表有三种:key转列表,value转列表,key和value同时转列表,格式为[(key,value),...]这样的格式。

1
2
3
4
5
6
7
8
9
10
# 列表转字典,列表元素必须为二元组
v = dict( [ ("k1", "v1"), ["k2", "v2"] ] )
print(v) # { "k1":"v1", "k2":"v2" }

# 字典转列表
info = { "age":12, "status":True, "name":"张亚飞" }
v1 = list(info) # ["age","status","name"]
v2 = list(info.keys()) # ["age","status","name"]
v3 = list(info.values()) # [12,True,"张亚飞"]
v4 = list(info.items()) # [ ("age",12), ("status",True), ("name","张亚飞") ]

我们已学了很多数据类型,在涉及多种数据类型之间的嵌套时,需注意一下几点:

  • 字典的键必须可哈希(list/set/dict不可哈希)。

  • 字典的值可以是任意类型。

  • 字典的键和集合的元素在遇到 布尔值 和 1、0 时,需注意重复的情况。

七、字典生成式

1
d = {key : value for (key,value) in iterable} 其中iterable是一个可迭代的对象,比如list。
  • 示例1: for 循环遍历列表,将列表中小元组的key和value取出,作为字典中的key:value

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import random

    students = {}

    for i in range(20):
    name = f'stu{i}'
    score = random.randint(60, 100)
    students[name] = score
    print(students)

    highscore = {}
    for name. score in students.items():
    if score > 90:
    highscore[name] = score
    print(highscore)

    print({name: score for name, score in students.items() if score > 90})
  • 示例2:字典的key和value值互换

    1
    2
    3
    dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
    dict2 = {v: k for k, v in dict1.items()}
    print(dict2) # {1: 'a', 2: 'b', 3: 'c', 4: 'd'}

八、知识扩展

1. 字典合并的几种方式

1
2
d1 = {'name': 'Tom', 'age': 20}
d2 = {'gpa': 4.0, 'is_single': True}
  • 方式一

    1
    2
    3
    4
    5
    6
    dnew = dict()
    for key, value in d1.items():
    dnew[key] = value
    for key, value in d2.items():
    dnew[key] = value
    # dnew == {'name': 'Tom', 'age': 20, 'gpa': 4.0, 'is_single': True}
  • 方式二

    1
    2
    dnew = d1.copy()
    dnew.update(d2)
  • 方式三

    1
    dnew = {**d1, **d2}
  • 方式四

    1
    dnew = dict(d1, **d2)
  • 方式五

    1
    2
    dnew = d1 | d2
    # dnew == {'name': 'Tom', 'age': 20, 'gpa': 4.0, 'is_single': True}

    这种联合运算符实际上在Python中不是新的, 它可以用于“合并”两个集合, 集合是无序且没有索引的集合,要用花括号括起来。

    1
    2
    3
    4
    a = {1, 2, 3}
    b = {3, 4, 5}
    print( a | b )
    # {1, 2, 3, 4, 5}

2. 默认字典

Python中通过Key访问字典,当Key不存在时,会引发‘KeyError’异常。为了避免这种情况的发生,可以使用collections类中的defaultdict()方法来为字典提供默认值.

示例1:字符计数

1
2
3
4
5
6
7
8
9
10
11
from collections import defaultdict

s = 'mississippi'
d = {}
for k in s:
if k in d:
d[k] += 1
else:
d[k] = 1
print(d)
# defaultdict(<class 'int'>, {'m': 1, 'i': 4, 's': 4, 'p': 2})

可用dict.setdefault()改进

1
2
3
4
5
6
7
8
9
from collections import defaultdict

s = 'mississippi'
d = {}
for k in s:
d.setdefault(k, 0)
d[k] += 1
print(d)
# defaultdict(<class 'int'>, {'m': 1, 'i': 4, 's': 4, 'p': 2})

进一步用默认字典collections.defaultdict改进,自动设置键和值

1
2
3
4
5
6
7
8
from collections import defaultdict

s = 'mississippi'
d = defaultdict(int)
for k in s:
d[k] += 1
print(d)
# defaultdict(<class 'int'>, {'m': 1, 'i': 4, 's': 4, 'p': 2})

示例2:分类

1
2
3
4
5
6
7
8
from collections import defaultdict

s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
d = defaultdict(list)
for k, v in s:
d[k].append(v)
print(d)
# defaultdict(<class 'list'>, {'yellow': [1, 3], 'blue': [2, 4], 'red': [1]})

示例3:构建set集合

1
2
3
4
5
6
7
8
from collections import defaultdict

s = [('red', 1), ('blue', 2), ('red', 3), ('blue', 4), ('red', 1), ('blue', 4)]
d = defaultdict(set)
for k, v in s:
d[k].add(v)
print(d)
# defaultdict(<class 'set'>, {'red': {1, 3}, 'blue': {2, 4}})

3. 字典排序

3.1 按key排序

1
2
3
4
5
6
7
8
9
10
11
12
dict1 = {'b': 2, 'd': 4, 'a': 1, 'c': 3}
l1 = sorted(dict1)
print(l1)
# ['a', 'b', 'c', 'd']
for key in l1:
print(key, dict1[key])
"""
a 1
b 2
c 3
d 4
"""

3.2 按value排序

1
2
3
4
dict1 = {'b': 2, 'd': 4, 'a': 1, 'c': 3}
dict2 = sorted(dict1.items(), key=lambda x: x[1])
print(dict2)
# [('a', 1), ('b', 2), ('c', 3), ('d', 4)]

3.3 字典列表排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
lis = [{ "name" : "Taobao", "age" : 100},  
{ "name" : "Runoob", "age" : 7 },
{ "name" : "Google", "age" : 100 },
{ "name" : "Wiki" , "age" : 200 }]

# 通过 age 升序排序
print ("列表通过 age 升序排序: ")
print (sorted(lis, key = lambda i: i['age']) )

print ("\r")

# 先按 age 排序,再按 name 排序
print ("列表通过 age 和 name 排序: ")
print (sorted(lis, key = lambda i: (i['age'], i['name'])) )

print ("\r")

# 按 age 降序排序
print ("列表通过 age 降序排序: ")
print (sorted(lis, key = lambda i: i['age'],reverse=True) )

"""
列表通过 age 升序排序:
[{'name': 'Runoob', 'age': 7}, {'name': 'Taobao', 'age': 100}, {'name': 'Google', 'age': 100}, {'name': 'Wiki', 'age': 200}]

列表通过 age 和 name 排序:
[{'name': 'Runoob', 'age': 7}, {'name': 'Google', 'age': 100}, {'name': 'Taobao', 'age': 100}, {'name': 'Wiki', 'age': 200}]

列表通过 age 降序排序:
[{'name': 'Wiki', 'age': 200}, {'name': 'Taobao', 'age': 100}, {'name': 'Google', 'age': 100}, {'name': 'Runoob', 'age': 7}]
"""

九、简单的总结

Python程序中的字典跟现实生活中字典非常像,允许我们以键值对的形式保存数据,再通过键索引对应的值。这是一种非常有利于数据检索的数据类型,底层原理我们在后续的课程中再研究。

赞赏一下吧~