第25~26讲:字典:当索引不好用时

一 字典https://www.cnblogs.com/linupython/p/5880280.html

1 概念/特点

  • 字典是另一种可变容器模型,且可存储任意类型对象。
  • 字典是python中唯一的映射类型,采用键值对(key-value)的形式存储数据。python对key进行哈希函数运算,根据计算的结果决定value的存储地址,所以字典是无序存储的,且key必须是可哈希的,可哈希表示键值不可变。

总结:

  • 字典值可以没有限制地取任何python对象,既可以是标准的对象,也可以是用户定义的,但键不行。
  • 不允许同一个键出现两次。创建时如果同一个键被赋值两次,后一个值会被记住
  • 键必须不可变,所以可以用数字,字符串或元组充当,所以用列表就不行

2 定义:字典可通过一对花括号来定义,元素内部通过冒号来连接key和对应的value,元素与元素之间通过逗号隔开,可进行自由嵌套。

3 语法格式:d = {key1 : value1, key2 : value2 }

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[, value])
  • 参数:
    • 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() 函数返回一个字典的浅复制。
  • 语法: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()

 

posted @ 2020-07-21 17:59  洛兰123  阅读(270)  评论(0编辑  收藏  举报