一 字典
1 概念/特点
- 字典是另一种可变容器模型,且可存储任意类型对象。
- 字典是python中唯一的映射类型,采用键值对(key-value)的形式存储数据。python对key进行哈希函数运算,根据计算的结果决定value的存储地址,所以字典是无序存储的,且key必须是可哈希的,可哈希表示键值不可变。
总结:
- 字典值可以没有限制地取任何python对象,既可以是标准的对象,也可以是用户定义的,但键不行。
- 不允许同一个键出现两次。创建时如果同一个键被赋值两次,后一个值会被记住
- 键必须不可变,所以可以用数字,字符串或元组充当,所以用列表就不行
2 定义:字典可通过一对花括号来定义,元素内部通过冒号来连接key和对应的value,元素与元素之间通过逗号隔开,可进行自由嵌套。
3 语法格式:dkey1value1, key2value2 }
4 字典的相关操作
- 创建
- 通过定义字典的过程创建 。 dict = {'a':1,'b':2,'c':3}
- dict()函数:也可以创建字典,工厂函数/类型函数
- 注意:在使用dict函数之前先把原来定义的dict字典给删了,用'del dict'命令,否则系统会报错:TypeError: 'dict' object is not callable
- 通过元组定义:dict1 = dict((('F',70),('i',105),('s',115),('h',104),('C',67)))
- 通过关键字定义:dict1 = dict(a = 97,b=98)
- 访问:通过在方括号内引用其键名来访问字典的元素。 print(dict['c'])
- 修改:
- 更改已有键值对的值:通过引用其键名来更改特定项的值。dict['a'] = 0
- 增加新的键值对:通过引用要添加的'key'并给该'key'赋值来添加新的键值对: dict['d']=4
- 删除:字典长度:获取字典的长度(键值对的个数),通过len方法实现。 len(dict)
- del语句:
- 删除整个字典。del dict
- 删除字典中的某个键值对。del dict['a']
- clear()方法:清空字典所有键值对,不需要参数。dict.clear()
- pop()方法:删除字典的某一键值对,其参数直接是键值key,不需要通过字典名称引用。dict.pop('c')
- popitem() 方法:删除最后插入的项目(在 3.7 之前的版本中,删除随机项目)。 dict.popitem()
- 字典长度:获取字典的长度(键值对的个数),通过len方法实现。len(dict)
- 字典排序:通过sorted方法对所有的key进行排序并以列表形式返回。 sorted(dict)
- 字典类型: 可用type函数查看字典中某一键值对的值的类型。type(dict['a'])
- 检查键值是否存在:要确定字典中是否存在指定的键,可使用 in 关键字。
1 >>> if 'a' in dict:
2 ... print("True")
- 遍历字典:可以使用 for 循环遍历字典,循环遍历字典时,返回值是字典的键,但也有返回值的方法。
- 逐个打印字典中的所有键名:
1 >>> for x in dict:
2 ... print(x)
- 逐个打印字典中的所有值:
1 >>> for x in dict:
2 ... print(dict[x])
- 还可以使用 values() 函数返回字典的值
1 >>> for x in dict.values():
2 ... print(x)
- 通过使用 items() 函数遍历键和值
>>> for x,y in dict.items():
... print(x,y)
5 字典的常用内置方法
(1)fromkeys()方法
- 描述:Python 字典 fromkeys() 函数用于创建一个新字典,以序列 seq 中元素做字典的键,value 为字典所有键对应的初始值。
- 语法:dict.fromkeys(seq[,])
- 参数:
- seq -- 字典键值列表。
- value -- 可选参数, 设置键序列(seq)的值,该参数只能有一个,并且所有键值列表对应的值都是它。
- 返回值:该方法返回一个新字典。
- 举例:
1 >>> dict1 = {}
2 >>> dict1.fromkeys((1,2,3))
3 {1: None, 2: None, 3: None}
4
5 >>> dict1.fromkeys((1,2,3),'Number')
6 {1: 'Number', 2: 'Number', 3: 'Number'}
7
8 >>> dict1.fromkeys((1,2,3),('one','two','three'))
9 {1: ('one', 'two', 'three'), 2: ('one', 'two', 'three'), 3: ('one', 'two', 'three')}
10
11 >>> dict1.fromkeys((1,3),'数字') # 无法修改字典的值,会返回一个新的字典
12 {1: '数字', 3: '数字'}
(2)keys()方法
- 描述:Python 字典(Dictionary) keys() 函数以列表返回一个字典所有的键。
- 语法:dict.keys()
- 参数:无参数
- 返回值:返回一个字典所有的键。
- 举例:
1 >>> dict1 = {'a':1,'b':2,'c':3}
2 >>> dict1.keys()
3 dict_keys(['a', 'b', 'c'])
(3)values()方法
- 描述:Python 字典(Dictionary) values() 函数以列表返回字典中的所有值。
- 语法:dict.values()
- 参数:无参数
- 返回值:返回字典中的所有值。
- 举例:
1 >>> dict1.values()
2 dict_values([1, 2, 3])
(4)items()方法
- 描述:Python 字典(Dictionary) items() 函数以列表返回可遍历的(键, 值) 元组数组。
- 语法:dict.items()
- 参数:无参数
- 返回值:返回可遍历的(键, 值) 元组数组。
- 举例:
1 >>> dict1.items()
2 dict_items([('a', 1), ('b', 2), ('c', 3)])
(5)get()方法
- 描述:Python 字典(Dictionary) get() 函数返回指定键的值,如果值不在字典中返回默认值。
- 语法:dict.get(key,default=None)
- 参数:
- key -- 字典中要查找的键。
- default -- 如果指定键的值不存在时,返回该默认值。
- 返回值:返回指定键的值,如果值不在字典中返回默认值None。
- 举例:>>> dict1.get('a')
(6)clear()方法:
- 描述:Python 字典(Dictionary) clear() 函数用于删除字典内所有元素。
- 语法:dict.clear()
- 参数:无参数
- 返回值:该函数没有任何返回值。
- 举例:
1 >>> dict1.clear()
2 >>> dict1
3 {}
(7)copy()方法
- 描述:Python 字典(Dictionary) copy() 函数返回一个字典的浅复制。
- 浅复制和赋值是不一样的,由以下例子可看出:dict3和dict2的id是不一样的。
1 >>> dict1 = {'a':1,'b':2,'c':3}
2 >>> dict2 = dict1
3 >>> dict3 = dict1.copy()
4 >>> id(dict1)
5 216304025856
6 >>> id(dict2)
7 216304025856
8 >>> id(dict3)
9 216275514752
- 语法:dict.copy()
- 参数:无参数
- 返回值:返回一个字典的浅复制。
- 举例:
1 >>> dict1.clear()
2 >>> dict1
3 {}
4 >>> dict2
5 {}
6 >>> dict3
7 {'a': 1, 'b': 2, 'c': 3}
(8)pop()方法
- 描述:Python 字典 pop() 方法删除字典给定键 key 及对应的值,返回值为被删除的值。key 值必须给出。 否则,返回 default 值。
- 语法:dict.pop(key[,default])
- 参数:
- key: 要删除的键值
- default: 如果没有 key,返回 default 值
- 返回值:返回被删除的值。
- 举例:
1 >>> dict3
2 {'a': 1, 'b': 2, 'c': 3}
3 >>> dict3.pop('c')
4 3
5 >>> dict3
6 {'a': 1, 'b': 2}
(9)popitem()方法
- 描述:Python 字典 popitem() 方法返回并删除字典中的最后一对键和值。如果字典已经为空,却调用了此方法,就报出 KeyError 异常。
- 语法:dict.popitem()
- 参数:无参数
- 返回值:返回一个键值对(key,value)形式。
1 >>> dict3
2 {'a': 1, 'b': 2}
3 >>> dict3.popitem()
4 ('b', 2)
5 >>> dict3
6 {'a': 1}
(10)setdefault()方法
- 描述:Python 字典 setdefault() 函数和 get()方法 类似, 如果键不存在于字典中,将会添加键并将值设为默认值/设置的值。
- 语法:dict.setdefault(key,default=None)
- 参数:
- key -- 查找的键值。
- default -- 键不存在时,设置的默认键值。
- 返回值:如果字典中包含有给定键,则返回该键对应的值,否则返回为该键设置的值
- 举例:
1 >>> dict3 = {'a':1}
2 >>> print(dict3.setdefault('b'))
3 None
4 >>> dict3
5 {'a': 1, 'b': None}
6 >>> print(dict3.setdefault('a',3))
7 1
8 >>> print(dict3.setdefault('c',3))
9 3
(11)update()方法
- 描述:Python 字典(Dictionary) update() 函数把字典dict2的键/值对更新到dict1里。
- 语法:dict1.update(dict2)
- 参数:dict2 -- 添加到指定字典dict1里的字典。
- 返回值:该方法没有任何返回值。
- 举例:
1 >>> dict3
2 {'a': 1, 'b': None, 'c': 3}
3 >>> dict1 = {'d':4,'e':5}
4 >>> dict3.update(dict1)
5 >>> dict3
6 {'a': 1, 'b': None, 'c': 3, 'd': 4, 'e': 5}
7 >>> dict1
8 {'d': 4, 'e': 5}
6 字典和列表的对比
字典的特殊性在于,内部存放的顺序与key存入的顺序没有任何关系,因为它本身就是无序的。字典与列表对比特点如下:
- 字典:
- 查找和插入的速度极快,不会随着key的增加而变慢
- 需要占用大量的内存(浪费比较多)
- 列表:
- 查找和插入的时间随着元素的增加而增加
- 占用空间小,内存浪费少
综上所述,字典是在用空间换取时间(执行效率)。
二 课后习题
(一)测试题部分
第25讲:
0. 当你听到小伙伴们在谈论“映射”、“哈希”、“散列”或者“关系数组”的时候,事实上他们就是在讨论什么呢?
答:是的,事实上他们就是在讨论我们这一讲介绍的“字典”,都是一个概念!
1. 尝试一下将数据('F': 70, 'C': 67, 'h': 104, 'i': 105, 's': 115)创建为一个字典并访问键 'C' 对应的值?
1 >>> MyDict = dict((('F', 70), ('i',105), ('s',115), ('h',104), ('C',67)))
2 >>> MyDict_2 = {'F':70, 'i':105, 's':115, 'h':104, 'C':67}
3 >>> type(MyDict)
4 <class 'dict'>
5 >>> type(MyDict_2)
6 <class 'dict'>
7 >>> MyDict['C']
8 67
2. 用方括号(“[]”)括起来的数据我们叫列表,那么使用大括号(“{}”)括起来的数据我们就叫字典,对吗?
1 >>> NotADict = {1, 2, 3, 4, 5}
2 >>> type(NotADict)
3 <class 'set'>
不难发现,虽然我们用大括号(“{}”)把一些数据括起来了,但由于没有反映出这些数据有映射的关系,所以创建出来的不是字典,而是叫’set’的东西,那’set’到底又是啥玩意儿呢?请看第027讲 | 集合:在我的世界里,你就是唯一!
3. 你如何理解有些东西字典做得到,但“万能的”列表却难以实现?
1 >>> brand = ['李宁', '耐克', '阿迪达斯', '鱼C工作室']
2 >>> slogan = ['一切皆有可能', 'Just do it', 'Impossible is nothing', '让编程改变世界']
3 >>> print('鱼C工作室的口号是:', slogan[brand.index('鱼C工作室')])
4 鱼C工作室的口号是: 让编程改变世界
列表brand、slogan的索引和相对的值是没有任何关系的,我们可以看出唯一有联系的就是两个列表间,索引号相同的元素是有关系的(品牌对应口号嘛),所以这里我们通过brand.index('鱼C工作室')这样的语句,间接的实现通过品牌查找对应的口号的功能。
这确实是一种可实现方法,呃……但用起来呢,多少有些别扭,效率还不高咧。况且Python是以简洁为主,这样子的实现肯定是不能让人满意的,所以呢,我们需要有字典这种映射类型的出现:
1 >>> dict1 = {'李宁':'一切皆有可能', '耐克':'Just do it', '阿迪达斯':'Impossible is nothing', '鱼C工作室':'让编程改变世界'}
2 >>> print('鱼C工作室的口号是:', dict1['鱼C工作室'])
3 鱼C工作室的口号是: 让编程改变世界
4. 下边这些代码,他们都在执行一样的操作吗?你看得出差别吗?
1 >>> a = dict(one=1, two=2, three=3)
2 >>> b = {'one': 1, 'two': 2, 'three': 3}
3 >>> c = dict(zip(['one', 'two', 'three'], [1, 2, 3]))
4 >>> d = dict([('two', 2), ('one', 1), ('three', 3)])
5 >>> e = dict({'three': 3, 'one': 1, 'two': 2})
答:是的,他们都在创建字典:a = dict(one=1, two=2, three=3),没有差别
5. 如图,你可以推测出打了马赛克部分的代码吗?
嗯,我很傻的用分片的方式一个一个写的,我好呆。。。(data[0:3],data[5:7],data[9:])
1 data = "1000,小甲鱼,男"
2 MyDict = {}
3 # 还记得字符串的分割方法吧,别学过就忘啦^_^
4 (MyDict['id'], MyDict['name'], MyDict['sex']) = data.split(',')
5
6 print("ID: " + MyDict['id'])
7 print("Name: " + MyDict['name'])
8 print("Sex " + MyDict['sex'])
第26讲:
0. Python的字典是否支持一键(Key)多值(Value)?
答:不支持,对相同的键再次赋值会将上一次的值直接覆盖。
1. 在字典中,如果试图为一个不存在的键(Key)赋值会怎样?
答:会自动创建对应的键(Key)并添加相应的值(Value)进去。(具体原理可以参考第3题的“扩展阅读”部分)
2. 成员资格操作符(in和not in)可以检查一个元素是否存在序列中,当然也可以用来检查一个键(Key)是否存在字典中,那么请问哪种的检查效率更高些?为什么?
答:在字典中检查键(Key)是否存在比在序列中检查指定元素是否存在更高效。因为字典的原理是使用哈希算法存储,一步到位,不需要使用查找算法进行匹配,因此时间复杂度是O(1),效率非常高。(关于如何使用哈希算法存储的具体原理可以参考第3题的“扩展阅读”部分)
3. Python对键(Key)和值(Value)有没有类型限制?
答:Python对键的要求相对要严格一些,要求它们必须是可哈希(Hash)的对象,不能是可变类型(包括变量、列表、字典本身等)。
但是Python对值是没有任何限制的,它们可以是任意的Python对象。
如果不清楚哈希原理以及字典的存放原理的童鞋,推荐阅读下小甲鱼帮你整理的这篇文章:你知道Python的字典(Dict)是如何存储的吗?(http://bbs.fishc.com/thread-45016-1-1.html)
4. 请目测下边代码执行后,字典dict1的内容是什么?
1 >>> dict1.fromkeys((1, 2, 3), ('one', 'two', 'three'))
2 >>> dict1.fromkeys((1, 3), '数字')
答:执行完成后,字典dict1的内容是:{1: '数字', 3: '数字'}
这里要注意的是,fromkeys方法是直接创建一个新的字典,不要试图使用它来修改一个原有的字典,因为它会直接无情的用把整个字典给覆盖掉。
5. 如果你需要将字典dict1 = {1: 'one', 2: 'two', 3: 'three'}拷贝到dict2,你应该怎么做?
答:可以利用字典的copy()方法:dict2 = dict1.copy(),在其他语言转移到Python小伙伴们刚开始可能会习惯性的直接用赋值的方法(dict2 = dict1),这样子做在Python中只是将对象的引用拷贝过去而已。
(二)动动手部分:
第25讲:
0. 尝试利用字典的特性编写一个通讯录程序吧!
我的代码:(感觉我的代码比小甲鱼的代码要聪明一点,哈哈哈哈)
1 # -*- coding:utf-8 -*-
2 def printInfo():
3 print("|--- 欢迎进入通讯录程序 ---|")
4 print("|--- 1:查询联系人资料 ---|")
5 print("|--- 2:插入新的联系人 ---|")
6 print("|--- 3:删除已有联系人 ---|")
7 print("|--- 4:退出通讯录程序 ---|")
8 print()
9
10 def find(addressDict):
11 print(addressDict)
12 name = input("请输入联系人姓名:")
13 if name in addressDict:
14 print(f"{name}:{addressDict[name]}")
15 return addressDict
16 else:
17 print("用户不存在,请重新选择相应操作!")
18 return addressDict
19
20 def insert(addressDict):
21 name = input("请输入联系人姓名:")
22 if name in addressDict:
23 print("您输入的姓名在通讯录中已存在",end = '')
24 print(f"-->> {name}:{addressDict[name]}")
25 i= input("是否修改用户资料(YES/NO):")
26 if i == YES:
27 addressDict[name]=input("请输入用户联系电话:")
28 return addressDict
29 else:
30 return main()
31 return addressDict
32 else:
33 addressDict[name]=input("请输入用户联系电话:")
34 return addressDict
35
36 def delete(addressDict):
37 name = input("请输入联系人姓名:")
38 if name in addressDict:
39 del addressDict[name]
40 return addressDict
41 else:
42 print("用户不存在,请重新选择相应操作!")
43 return addressDict
44
45 def over():
46 print("|--- 感谢使用通讯录程序 ---|")
47 exit()
48
49 def main():
50 addressDict = {}
51 printInfo()
52 while 1:
53 print(f"addressDict = {addressDict}")
54 num = int(input("请输入相关的指令代码:"))
55
56 if num == 1:
57 find(addressDict)
58 elif num == 2:
59 insert(addressDict)
60 elif num == 3:
61 delete(addressDict)
62 elif num == 4:
63 over()
64 else:
65 print("输入错误,请重新输入数字:1或2或3或4 选择相应的操作")
66 print()
67
68 main()
小甲鱼的代码:
1 print('|--- 欢迎进入通讯录程序 ---|')
2 print('|--- 1:查询联系人资料 ---|')
3 print('|--- 2:插入新的联系人 ---|')
4 print('|--- 3:删除已有联系人 ---|')
5 print('|--- 4:退出通讯录程序 ---|')
6
7 contacts = dict()
8
9 while 1:
10 instr = int(input('\n请输入相关的指令代码:'))
11
12 if instr == 1:
13 name = input('请输入联系人姓名:')
14 if name in contacts:
15 print(name + ' : ' + contacts[name])
16 else:
17 print('您输入的姓名不再通讯录中!')
18
19 if instr == 2:
20 name = input('请输入联系人姓名:')
21 if name in contacts:
22 print('您输入的姓名在通讯录中已存在 -->> ', end='')
23 print(name + ' : ' + contacts[name])
24 if input('是否修改用户资料(YES/NO):') == 'YES':
25 contacts[name] = input('请输入用户联系电话:')
26 else:
27 contacts[name] = input('请输入用户联系电话:')
28
29 if instr == 3:
30 name = input('请输入联系人姓名:')
31 if name in contacts:
32 del(contacts[name]) # 也可以使用dict.pop()
33 else:
34 print('您输入的联系人不存在。')
35
36 if instr == 4:
37 break
38
39 print('|--- 感谢使用通讯录程序 ---|')
第·26讲:
0. 尝试编写一个用户登录程序(这次尝试将功能封装成函数)
小甲鱼代码:
1 user_data = {}
2
3 def new_user():
4 prompt = '请输入用户名:'
5 while True:
6 name = input(prompt)
7 if name in user_data:
8 prompt = '此用户名已经被使用,请重新输入:'
9 continue
10 else:
11 break
12
13 passwd = input('请输入密码:')
14 user_data[name] = passwd
15 print('注册成功,赶紧试试登录吧^_^')
16
17 def old_user():
18 prompt = '请输入用户名:'
19 while True:
20 name = input(prompt)
21 if name not in user_data:
22 prompt = '您输入的用户名不存在,请重新输入:'
23 continue
24 else:
25 break
26
27 passwd = input('请输入密码:')
28 pwd = user_data.get(name)
29 if passwd == pwd:
30 print('欢迎进入XXOO系统,请点右上角的X结束程序!')
31 else:
32 print('密码错误!')
33
34 def showmenu():
35 prompt = '''
36 |--- 新建用户:N/n ---|
37 |--- 登录账号:E/e ---|
38 |--- 推出程序:Q/q ---|
39 |--- 请输入指令代码:'''
40
41 while True:
42 chosen = False
43 while not chosen:
44 choice = input(prompt)
45 if choice not in 'NnEeQq':
46 print('您输入的指令代码错误,请重新输入:')
47 else:
48 chosen = True
49
50 if choice == 'q' or choice == 'Q':
51 break
52 if choice == 'n' or choice == 'N':
53 new_user()
54 if choice == 'e' or choice == 'E':
55 old_user()
56
57 showmenu()