《100天精通Python——基础篇 2025 第9天:字典操作全解析与哈希原理揭秘》
目录
- 一、初始化
- 1.1 {}--直接创建字典
- 1.2 dict()函数--创建字典
- 1.3 fromkeys()方法--创建一个新字典
- 二、元素访问
- 2.1 使用中括号[]语法
- 2.2 get()方法--获取字典中指定键的值
- 2.3 setdefault()方法--获取字典中指定键的值
- 三、新增和修改
- 3.1 直接赋值
- 3.2 update()方法--更新字典
- 3.3 使用“|”合并映射
- 四、删除
- 4.1 del--删除字典或字典中指定的键
- 4.2 pop()方法--删除字典中指定键对应的键值对并返回被删除的值
- 4.3 popitem()方法--返回并删除字典中的键值对
- 4.4 clear()方法--删除字典中的全部元素
- 五、遍历
- 5.1 items()方法--获取字典的所有"键值对"
- 5.2 keys()方法--获取字典的所有键
- 5.3 values()方法--获取字典的所有值
- 5.4 遍历与删除
- 5.5 字典视图
- 六、字典的key
- 6.1 “可哈希”指什么
- 6.2 哈希表(散列表)是什么?
- 6.2.1 哈希表的构建
- 6.2.2 哈希函数的构造
- 6.2.3 处理冲突的方法
- 七、有序性
- 八、小结
- 九、综合练习----序列的应用
今天我们将深入学习 Python 中非常重要的数据结构 —— 字典(dict)。从字典的基础定义、增删改查操作,到进阶的遍历、合并技巧,逐步掌握字典的常用操作。同时,我们还会拓展讲解字典背后的哈希表原理,理解什么是可哈希(hashable)对象,字典如何通过哈希快速查找数据,帮助你打下扎实的底层基础。通过今天的学习,你不仅可以熟练使用字典,还能理解字典为何如此高效,为后续深入学习 Python 的性能优化、底层数据结构打下坚实基础。
一、初始化
在 Python 中,字典与列表类似,也是可变数据结构,不过与列表不同,它是无序的,保存的内容是以 键-值对
的形式存放的。这类似于我们的新华字典,它可以把拼音和汉字关联起来,通过音节表可以快速找到想要的汉字。其中新华字典里的音节表相当于键(key),而对应的汉字,相当于值(value)。键是唯一的,而值可以有多个。字典在定义一个包含多个命名字段的对象时,很有用。字典的主要特征如下:
- 无序的数据结构。字典内的数据随机排列。
- 可变的数据类型。可以随意添加、删除和更新字典内的数据,字典对象会自动伸缩,确保内部数据无缝隙排列在一起。
- 通过键映射访问内部数据,不能够通过下标索引访问内部数据。字典有时也称为关联数组或者散列表(hash)。它是通过键将一系列的值联系起来的,这样就可以通过键从字典中获取指定项,但不能通过索引来获取。
- 内部数据统称为元素(Item,也称为 Entry),每个元素都由键和值组成。
- 键名必须是可哈希的,即确保键名的唯一性、不可变性。值可以为任意类型的数据。ps:不允许同一个键出现两次,如果出现两次,则后一个值会被记住。
- 字典的字面值使用大括号包含所有元素,元素之间使用逗号隔开,键和值之间使用冒号分隔。
1.1 {}–直接创建字典
定义字典时,每个元素都包含两个部分 键
和 值
。以水果名称和价格的字典为例,键为水果名称,值为水果价格,如下图所示:
在创建字典时,使用一对大括号 "{}"
,在大括号中间放置使用冒号分隔的 键
和 值
作为元素,相邻两个元素使用逗号分隔。其语法格式如下:
dictionary = {'key1':'value1', 'key2':'value2', …, 'keyn':'valuen',}
参数说明:
1.dictionary: 表示字典名称;只要是符合python标识符命名规则即可
2.key1、key2…keyn: 表示元素的键,必须是唯一的,并且不可变,例如可以是字符串、数字或者元组
3.value1、value2…valuen: 表示元素的值,可以是任何数据类型,不是必须唯一。
示例代码:
# 键为字符串的字典
link = {'name': 'amo', 'age': 18, 'height': 1.68, 'address': '重庆市', 'has_girlfriend': False}
record = {'english': 97, 'chinese': 99, 'python': 100, 'c': 96} # 定义值为数字的字典
language = {'python': ['优雅', '明确', '简单'], 'java': ['继承', '封装', '多态']} # 定义值为列表的字典
student = {1: '明日', 2: '零语', 3: '惜梦'} # 定义键为数值的字典
temp = {('a', 'b'): ('1000', '1001')} # 定义键为元组的字典
dic1 = {'name': 'amo', 2: 'a', 13.14: '520', True: 1} # 定义混合类型的字典
dic2 = {'name': 'amo', 2: 'a', 13.14: '520', 1: '数值', True: '布尔值'} # 定义混合类型的字典
dic3 = {} # 定义空字典
# 映射解构 ** 可在 dict 字面量中使用,同样可以多次使用
print({'a': 0, **{'x': 1}, 'y': 2, **{'z': 3, 'x': 4}})
print('link字典: ', link)
print('record字典: ', record)
print('language字典: ', language)
print('student字典: ', student)
print('temp字典: ', temp)
print('混合类型字典: ', dic1)
print('混合类型字典: ', dic2)
print('空字典: ', dic3)
1.2 dict()函数–创建字典
dict() 函数用于创建一个字典对象,其语法格式如下:
In [1]: dict?
Init signature: dict(self, /, *args, **kwargs)
Docstring:
dict() -> new empty dictionary
dict(mapping) -> new dictionary initialized from a mapping object's(key, value) pairs
dict(iterable) -> new dictionary initialized as if via:d = {}for k, v in iterable:d[k] = v
dict(**kwargs) -> new dictionary initialized with the name=value pairsin the keyword argument list. For example: dict(one=1, two=2)
Type: type
参数说明:
1.**kwargs: 一到多个关键字参数,如dict(one=1, two=2, three=3)
2.mapping: 元素容器,如zip函数
3.iterable: 可迭代对象
4.返回值: 一个字典。如果不传入任何参数时,则返回空字典;
示例代码:
# 1.创建字典
dict1 = dict()
print('创建空字典: ', dict1)
# 通过给定"键-值对"的方式来创建字典
dict1 = dict(刘能='刘', 赵四='赵', 谢广坤='谢', 王长贵='王')
print(dict1)
# 2.通过给定的关键字参数创建字典
# 通过给定的关键字参数创建字典
d1 = dict(name='Amo') # 字符串的key与值
print(d1) # 打印d1字典内容
d2 = dict(Python=98, English=78, Math=81) # 字符串的key,int类型的值
print(d2) # 打印d2字典内容
d3 = dict(name='Tom', age=21, height=1.5) # 字符串的key,多种类型的值
print(d3) # 打印d3字典内容
# ps: 使用dic()函数通过给定的关键字参数创建字典时,name键名必须都是Python中的标识符,否则会提示SyntaxError。
"""
In [2]: dict(1='a', 2='b')Cell In[2], line 1dict(1='a', 2='b')^
SyntaxError: expression cannot contain assignment, perhaps you meant "=="?
In [3]: dict(a=1,b=2)
Out[3]: {'a': 1, 'b': 2}
"""
# 3.通过传入映射函数创建字典
# 通过映射函数创建字典
a1 = dict(zip((1, 2, 3, 4), ('杰夫•贝佐斯', '比尔•盖茨', '沃伦•巴菲特', '伯纳德•阿诺特')))
print(a1) # 打印a1字典内容,key为int类型,值为字符串类型
a2 = dict(zip(['one', 'two', 'three'], [1, 2, 3]))
print(a2) # 打印a2字典内容,key为字符串类型,值为int类型
a3 = dict(zip(['北京', '上海', '长春'], ['晴', '大雨', '小雪']))
print(a3) # 打印a3字典内容,key与值都是字符串类型# 通过传入可迭代对象创建字典
# 通过可迭代对象创建字典
b1 = dict([('Monday', 1), ('Tuesday', 2), ('Wednesday', 3)]) # 元组对象
print(b1) # 打印b1字典内容
b2 = dict([['apple', 6.5], ['orange', 3.98], ['pear', 8], ['banana', 2.89]]) # 列表对象
print(b2) # 打印b2字典内容
b2 = dict([['apple', 6.5], ['orange', 3.98], ['pear', 8], ['banana', 2.89]], a=3, b=4) # 也是可以这样写的
print(b2)
1.3 fromkeys()方法–创建一个新字典
字典对象中的 fromkeys() 方法用于创建一个新的字典。以序列 seq 中的元素作为字典的键,用 value 作为字典所有键对应的值。其语法格式如下:
In [4]: dict.fromkeys?
Signature: dict.fromkeys(iterable, value=None, /)
Docstring: Create a new dictionary with keys from iterable and values set to value.
Type: builtin_function_or_method
参数说明:
1.dict: 字典对象
2.seq: 序列(如字符串、列表、元组等),作为新字典的键
3.value: 可选参数,如果提供value值则该值将被设置为字典的值,字典所有键对应同一个值,如果不提供value值,则默认返回None。
4.返回值: 返回一个新字典
示例代码:
# 创建字典中所有值为None的字典
dict1 = dict.fromkeys(['hour', 'minute', 'second']) # 创建新的字典
print(dict1) # 打印所有值都为None的字典
# 创建一个所有值为相同值的字典
dict2 = dict.fromkeys(['小明', '李四'], 1) # 两名同学考试并列第一
print(dict2) # 打印相同值的字典
# 循环创建多个同值字典
a = [1, 2, 3] # 键列表
b = [6, 7, 8] # 值列表
for i in b: # 循环更换值print(dict.fromkeys(a, i))
二、元素访问
2.1 使用中括号[]语法
使用 print() 函数可以查看字典的数据结构,而使用 中括号[]
语法可以直接访问字典的元素。语法格式如下:
dict[key] # dict表示字典对象,key表示键
示例代码:
dict1 = {'a': 1, 'b': 2, 'c': 3} # 定义字典
print(dict1['a']) # 访问键为a的元素,输出为1
# 在使用该方法获取指定键的值时,如果指定的键不存在,就会抛出异常。
print(dict1['e']) # KeyError: 'e'
# 所以在使用[]方法的时候,我们可以先使用 in 关键字判断key是否存在
2.2 get()方法–获取字典中指定键的值
在使用字典时,很少直接输出它的内容。一般需要根据指定的键得到相应的结果。Python 中推荐的方法是使用字典对象的 get() 方法获取指定键的值。其语法格式如下:
In [6]: dict.get?
Signature: dict.get(self, key, default=None, /)
Docstring: Return the value for key if key is in the dictionary, else default.
Type: method_descriptor
参数说明:
1.dict: 字典对象,即要从中获取值的字典
2.key: 字典中要查找的键
3.default: 可选参数,当指定的键不存在时,返回默认值,如果省略default参数,则返回None。
4.返回值: 如果字典中键存在,则返回键所对应的值;如果键不存在,则返回default默认值。
示例代码:
import random # 导入随机模块# 获取字典中键对应的值
dic1 = {'北京': '晴', '上海': '阵雨转晴', '广州': '阴'}
print(dic1) # 输出字典
print('北京的天气为:', dic1.get('北京')) # 获取"北京"的天气报告,返回键对应的值
# 与setdefault()方法不同 提示:get()方法只是用于获取键的值,并不会添加键到字典。
# 获取字典中不存在的键所对应的值
dic2 = {'name': 'Tom', 'age': 21} # 创建字典
# 通过get()方法访问一个字典里的没有的键,返回None,也可自定义默认值
print(dic2.get('height')) # 返回None
print(dic2.get('height', '165cm')) # 返回165cm
# 如果原字典中存在某个key对应的值为None,需要特别小心,所以最好在使用get()方法时来个缺省值
# 获取字典中最小键所对应的值
# 公司内部运动成绩排名
dict_info = {1: '小王', 2: '老黄', 3: '小李', 4: '小张', 5: '老刘'}
minimal_key = min(dict_info.keys(), key=lambda i: i) # 获取字典数据中最小的键
print('公司内容运动会第一名是:', dict_info.get(minimal_key)) # 根据最小的键获取对应的值
print('-----------------------------------------------------')
name = ['绮梦', '冷伊一', '香凝', '黛兰'] # 作为键的列表
sign_person = ['水瓶座', '射手座', '双鱼座', '双子座'] # 作为值的列表
person_dict = dict(zip(name, sign_person)) # 转换为个人字典
sign_all = ['白羊座', '金牛座', '双子座', '巨蟹座', '狮子座', '处女座', '天秤座', '天蝎座', '射手座', '摩羯座','水瓶座', '双鱼座']
nature = ['有一种让人看见就觉得开心的感觉,阳光、乐观、坚强,性格直来直往,就是有点小脾气。','很保守,喜欢稳定,一旦有什么变动就会觉得心里不踏实,性格比较慢热,是个理财高手。','喜欢追求新鲜感,有点小聪明,耐心不够,因你的可爱性格会让很多人喜欢和你做朋友。','情绪容易敏感,缺乏安全感,做事情有坚持到底的毅力,为人重情重义,对朋友和家人特别忠实。','有着宏伟的理想,总想靠自己的努力成为人上人,向往高高在上的优越感,也期待被仰慕被崇拜的感觉。','坚持追求自己的完美主义者。','追求平等、和谐,擅于察言观色,交际能力很强,因此真心的朋友不少。最大的缺点就是面对选择总是犹豫不决。','精力旺盛,占有欲强,对于生活很有目标,不达目的誓不罢休,复仇心重。','崇尚自由,勇敢、果断、独立,身上有一股勇往直前的劲儿,只要想做,就能做。','是最有耐心的,为事最小心,也是最善良的星座。做事脚踏实地,也比较固执,不达目的不放手,而且非常勤奋。','人很聪明,最大的特点是创新,追求独一无二的生活,个人主义色彩很浓重的星座。','集所有星座的优缺点于一身。最大的优点是有一颗善良的心,愿意帮助别人。']sign_dict = dict(zip(sign_all, nature)) # 转换为星座字典
name_str = random.choice(name) # 随机获取名字
print('【', name_str, '】的星座是', person_dict.get(name_str)) # 输出星座
print('\n 她的性格特点是:\n\n', sign_dict.get(person_dict.get(name_str))) # 输出性格特点
2.3 setdefault()方法–获取字典中指定键的值
字典 setdefault() 方法和 get() 方法类似,用于获取字典中指定键的值。如果键不在字典中,则会添加键到字典且将 default 值设为该键的值,并返回该值。其语法格式如下:
In [5]: dict.setdefault?
Signature: dict.setdefault(self, key, default=None, /)
Docstring:
Insert key with a value of default if key is not in the dictionary.Return the value for key if key is in the dictionary, else default.
Type: method_descriptor
参数说明:
1.dict: 字典对象,即要从中获取值的字典。
2.key: 字典中要查找的键。
3.default: 可选参数,如果指定键的值不存在时,返回该值,默认为None。
4.返回值: 如果键在字典中,则返回键所对应的值。如果键不在字典中,则向字典中添加这个键,且设置default为这个键的值,
并返回default值。
注意: 在使用字典 setdefault() 方法时需要注意,如果指定的键存在,仍设置了 default 值,default 值不会覆盖原来已经存在的键所对应的值。示例代码:
# 获取空字典的值
dict1 = {} # 定义空字典
print(dict1.setdefault('name')) # # 获取空字典中的值 None
print(dict1) # 输出dict1 {'name': None}
dict2 = {} # 定义空字典
dict2.setdefault('name', 'Anna') # 在没有获取到name键与对应的值时,为字典添加默认数据
print(dict2) # 打印结果
dict3 = {'name': 'amo'}
print(dict3.setdefault('name', 'gg')) # key值name存在,故返回其对应的值amo
print(dict3)
三、新增和修改
3.1 直接赋值
由于字典是可变数据结构,所以可以随时在字典中添加 键-值对
。 向字典中添加元素的语法格式如下:
dictionary[key] = value
参数说明:
1.dictionary: 表示字典名称
2.key: 表示要添加元素的键,必须是唯一的,并且不可变,例如可以是字符串、数字或者元组
3.value: 表示元素的值,可以是任何数据类型,不是必须唯一的
示例代码:
dictionary = dict((('绮梦', '水瓶座'), ('冷伊一', '射手座'), ('香凝', '双鱼座'), ('黛兰', '双子座')))
dictionary["碧琦"] = "巨蟹座" # 添加一个元素
# {'绮梦': '水瓶座', '冷伊一': '射手座', '香凝': '双鱼座', '黛兰': '双子座', '碧琦': '巨蟹座'}
print(dictionary)
从上面的结果中可以看到,字典中又添加了一个键为 "碧琦"
的元素。由于在字典中,"键"
必须是唯一的,如果新添加元素的 "键"
与已经存在的 "键"
重复,那么将使用新的 "值"
替换原来该 "键"
的值,这也相当于 修改字典的元素。 例如,再添加一个 "键"
为 "香凝"
的元素,设置她的星座为 "天蝎座"
。可以使用下面的代码:
dictionary = dict((('绮梦', '水瓶座'), ('冷伊一', '射手座'), ('香凝', '双鱼座'), ('黛兰', '双子座')))
dictionary["香凝"] = "天蝎座" # 添加一个元素,当元素存在时,则相当于修改功能
# {'绮梦': '水瓶座', '冷伊一': '射手座', '香凝': '天蝎座', '黛兰': '双子座'}
print(dictionary)
从上面的结果可以看到,字典中并没有添加一个新的 "键"
―"香凝"
,而是直接对 "香凝"
进行了修改,"香凝"
对应的值变为了 "天蝎座"
。
3.2 update()方法–更新字典
字典中的 update() 方法用于更新字典,其参数可以是字典或者某种可迭代的数据类型。其语法格式如下:
In [10]: dict.update?
Docstring:
D.update([E, ]**F) -> None. Update D from dict/iterable E and F.
If E is present and has a .keys() method, then does: for k in E: D[k] = E[k]
If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v
In either case, this is followed by: for k in F: D[k] = F[k]
Type: method_descriptor
示例代码:
# 将一个字典的键值对更新到(添加到)另一个字典中
dic1 = {'a': 1, 'b': 2}
print('更新前:', dic1) # 输出更新前的字典内容
dict2 = {'c': 3}
dic1.update(dict2) # 将字典dict2中的"键值对"添加到字典dict中
print('更新后:', dic1) # 输出更新后的字典内容
# 以元组为参数更新字典
dict1 = {'apple': 5.98, 'banana': 3.68}
print('更新前:', dict1) # 输出更新前的字典内容
list1 = [('pear', 3.00), ('watermelon', 2.89)] # 列表中的每个元组是一个键值对
dict1.update(list1) # 更新字典
print('更新后:', dict1) # 输出更新后的字典内容
'''
提示:如果字典update()方法的参数是可迭代对象,则可迭代对象中的每一项自身必须是可迭代的,
并且每一项只能有两个对象。第一个对象将作为新字典的键,第二个对象将作为其键对应的值。
'''
# 更新字典中相同的键
dict1 = {'apple': 5.98, 'banana': 3.68}
print('更新前:', dict1) # 输出更新前的字典内容
# 如果两个字典中有相同的键,则字典dict2中的键值将覆盖源字典dict1中的键值
dict2 = {'apple': 8.89, 'pear': 3.00}
dict1.update(dict2) # 将字典dict2中的"键值对"添加到字典dict1中
print('更新后:', dict1) # 输出更新后的字典内容
# 更新原字典中不存在的键值对
# 小明第一次学习的单词
dict1 = {'Tiger': '老虎', 'Giraffe': '长颈鹿', 'Lion': '狮子'}
# 第二次复习了第一次的两个单词,又新学习了两个单词
dict2 = {'Tiger': '老虎', 'Lion': '狮子', 'Horse': '马', 'Bear': '熊'}
for i in dict2.items(): # 遍历dict2中的键值对if i not in list(dict1.items()): # 如果dict2中的键值对不存在dict1当中dict1.update([i]) # 就将不存在的键值对更新至dict1中
print('小明共学习了以下这些单词:\n', dict1)
3.3 使用“|”合并映射
Python 3.9 支持使用 |
和 |=
合并映射。这不难理解,因为二者也是并集运算符。|
运算符创建一个新映射。示例代码:
d1 = {'a': 1, 'b': 3, 'd': 7}
d2 = {'a': 2, 'b': 4, 'c': 6}
# 通常,新映射的类型与左操作数(本例中的 d1)的类型相同
print(d1, d2)
print(d1 | d2)
print(d1, d2)
d1 |= d2
print(d1, d2)
四、删除
4.1 del–删除字典或字典中指定的键
在实现字典内容的删除时,可以使用 del 删除整个字典或字典中指定的键。使用 del 删除字典的语法格式如下:
del dict[key] # 删除字典中指定的键
del dict # 删除字典
参数说明:
1.dict: 字典对象
2.key: 要删除的键
示例代码:
# 先删除字典中指定的键再删除整个字典
dic1 = {'一等奖': '500万元', '二等奖': '100万元', '参与奖': '零食大礼包一份'}
print('删除前:', dic1)
del dic1['参与奖'] # 删除指定键
print('删除后:', dic1)
del dic1 # 删除字典
# 当使用del命令删除一个字典中不存在的键时,将抛出KeyError异常
dic1 = {'一等奖': '500万元', '二等奖': '100万元', '参与奖': '零食大礼包一份'}
# 当删除一个不存在的键时,将抛出KeyError异常
# del dic1['三等奖'] # 提示KeyError: '三等奖'
# 为防止删除不存在的键时抛出异常,可以使用操作符in先判断指定键是否存在与字典中,然后再使用del命令删除指定的键
dic2 = {'一等奖': '500万元', '二等奖': '100万元', '参与奖': '零食大礼包一份'}
if '参与奖' in dic2: # 返回Truedel dic2['参与奖'] # 删除指定键
print(dic2)
4.2 pop()方法–删除字典中指定键对应的键值对并返回被删除的值
字典中的 pop() 方法用于删除字典内指定键所对应的键值对,并返回被删除的值。指定的键如果不在字典中,则必须设置一个 default 值,否则会报错,此时返回的就是 default 值。其语法格式如下:
In [9]: dict.pop?
Docstring:
D.pop(k[,d]) -> v, remove specified key and return the corresponding value.If the key is not found, return the default if given; otherwise,
raise a KeyError.
Type: method_descriptor
参数说明:
1.D: 字典对象
2.key: 指定字典中要删除的键
3.default: 可选参数,指定的键不在字典中,必须设置default默认值,否则会报错
4.返回值: 如果指定的键在字典中,则返回指定键所对应的值,否则返回设置的default值。
示例代码:
# 删除字典中指定键所对应的键值对,并返回被删除的值
d = {'壹': 1, '贰': 2, '叁': 3} # 创建字典
print('删除前: ', d)
print(d.pop('贰')) # 返回键所对应的值
print('删除后: ', d)
# 删除字典中指定键所对应的键值对,并设置default值
dic1 = {'name': 'amo', 'age': 18, 'height': 1.68} # 创建字典
print('删除前: ', dic1)
# 键不在字典中,必须设置default值,返回default值
print(dic1.pop('qq', '123456789'))
# 注意:键在字典中,设置default值,此时default值无效,返回的是源字典中键对应的值
print(dic1.pop('name', 'paul')) # 返回amo
print('删除后: ', dic1)
4.3 popitem()方法–返回并删除字典中的键值对
字典中的 popitem() 方法用于返回并删除字典中的一个键值对(一般删除字典末尾的键值对)。其语法格式如下:
In [8]: dict.popitem?
Signature: dict.popitem(self, /)
Docstring:
Remove and return a (key, value) pair as a 2-tuple.Pairs are returned in LIFO (last-in, first-out) order.
Raises KeyError if the dict is empty.
Type: method_descriptor
参数说明:
1.dict: 字典对象
2.返回值: 返回一个(key,value)形式的键值对
示例代码:
# 返回并删除字典中的一个键值对
dict1 = {'001': '小王', '002': '小张', '003': '小梁'} # 创建字典
print(dict1.popitem()) # 删除键值
print(dict1)
# 删除空字典中的键值对
dict1 = {'001': '小王', '002': '小张', '003': '小梁'} # 创建字典
dict1.clear() # 删除字典中的全部元素
# 如果字典为空,却调用了popitem()方法,会提示KeyError异常
print(dict1.popitem()) # 提示KeyError: 'popitem(): dictionary is empty'
# 分别获取返回并删除字典中的键与值
student = {'name': 'Amo', 'age': '18'} # 创建字典
key, value = student.popitem() # 删除键值对
print('随机删除的键为:', key)
print('随机删除的值为:', value)
4.4 clear()方法–删除字典中的全部元素
字典中的 clear() 方法用于删除字典内的全部元素。执行 clear() 方法后,原字典将变为空字典。其语法格式如下:
In [7]: dict.clear?
Docstring: D.clear() -> None. Remove all items from D.
Type: method_descriptor
参数说明:
1.D: 字典对象
2.返回值: None
示例代码:
# 删除字典中的全部元素
d = {'壹': 1, '贰': 2, '叁': 3} # 创建字典
print('删除前:', d)
d.clear() # 删除字典中的全部元素
print('删除后:', d)
# 清空通过推导式所创建的字典
# 生成由数字和数字转换成的字符串组成的字典
dict1 = {i: '000' + str(i) for i in range(1, 6)} # 0~5(包括5)
print('清空前:', dict1)
dict1.clear() # 清空字典
print('清空后:', dict1)
五、遍历
5.1 items()方法–获取字典的所有"键值对"
使用字典对象的 items() 方法可以获取字典的所有 键值对
。 返回的是一个可迭代对象,可以使用 list() 函数转换为列表。其语法格式如下:
In [11]: dict.items?
Docstring: D.items() -> a set-like object providing a view on D's items
Type: method_descriptor参数说明:
1.dict: 字典对象
2.返回值: 返回一个可迭代对象
示例代码:
# 获取字典中所有"键值对"
dictionary = {'hour': 3, 'minute': 45, 'second': 21} # 创建字典
print(dictionary.items()) # 获取字典的键值对,返回一个可迭代对象
print(list(dictionary.items())) # 使用list()函数转换为列表
# 循环遍历字典中的全部"键值对"
dictionary = {'语文': 98, '数学': 95, '英语': 88} # 创建字典
for item in dictionary.items(): # 通过for循环获取字典中的全部"键值对"print(item)
# 通过for循环分别遍历字典中的键和值
dictionary = {'2017年': 1682, '2018年': 2135} # 创建字典
for key, value in dictionary.items(): # 通过for循环获取字典中具体的每个键和值print(key, '天猫双十一当天成交额:', value, '亿元')# 根据给出的值获取对应的键
dict_name = {'刘能': '刘', '赵四': '赵', '谢广坤': '坤'} # 名字数据def get_key(dic, val): # 自定义根据值获取键的函数return [k for (k, v) in dic.items() if v == val] # 列表推导式获取指定值对应的键key = get_key(dict_name, '赵') # 调用函数获取“赵”对应的键
print('"赵"对应的键为:', key[0])
5.2 keys()方法–获取字典的所有键
字典 keys() 方法用于获取一个字典所有的键。返回的是一个可迭代对象,可以使用 list() 函数转换为列表。其语法格式如下:
In [12]: dict.keys?
Docstring: D.keys() -> a set-like object providing a view on D's keys
Type: method_descriptor
参数说明:
1.D: 字典对象
2.返回值: 返回一个可迭代对象
示例代码:
# 获取字典中的所有键
dictionary = {'hour': 3, 'minute': 45, 'second': 21} # 创建字典
print(dictionary.keys()) # 获取字典中所有的键,返回一个可迭代对象
print(list(dictionary.keys())) # 使用list()函数转换为列表
# 循环遍历字典中的键
dictionary = {'杰夫•贝佐斯': 1, '比尔•盖茨': 2, '沃伦•巴菲特': 3, '伯纳德•阿诺特': 4} # 创建字典
for key in dictionary.keys(): # 通过for循环获取字典中的具体的key(键)print(key)
for key in dictionary:print(key) # 默认就遍历字典的key
# 通过keys()方法获取字典中所有的值
# 字典数据
dict_demo = {1: '杰夫•贝佐斯', 2: '比尔•盖茨', 3: '沃伦•巴菲特', 4: '伯纳德•阿诺特'}
for i in dict_demo.keys(): # 遍历字典中所有键# value = dict_demo.get(i) # 获取键对应的值value = dict_demo[i] # 获取键对应的值print('字典中的值有:', value)
5.3 values()方法–获取字典的所有值
字典 values() 方法用于获取一个字典所有的值。返回的是一个可迭代对象,可以使用 list() 函数转换为列表。其语法格式如下:
In [13]: dict.values?
Docstring: D.values() -> an object providing a view on D's values
Type: method_descriptor
参数说明:
1.D: 字典对象
2.返回值: 返回一个可迭代对象
示例代码:
# 获取字典中所有值
dictionary = {'hour': 3, 'minute': 45, 'second': 21} # 创建字典
print(dictionary.values()) # 获取字典的所有值,返回一个可迭代对象
print(list(dictionary.values())) # 使用list()函数转换为列表
# 循环遍历获取字典中的值
dictionary = {'小英子': '3月5日', '阳光': '12月8日', '茜茜': '7月6日', } # 创建字典
for value in dictionary.values(): # 通过for循环获取字典中具体的value(值)print(value)
# 通过lambda表达式获取字典中指定条件的值
dict_demo = {'apple': 6.5, 'orange': 3.98, 'pear': 8, 'banana': 2.89}
max_value = max(dict_demo.values(), key=lambda i: i) # 获取字典中所有值最大的那个
print('字典中值最大的为:', max_value)
# 获取字典中所有float类型的值
type_value = list(filter(lambda i: isinstance(i, float), dict_demo.values()))
print('字典中所有float类型的值为:', type_value)
# 获取字典中长度为3的那个值
len_value = list(filter(lambda i: len(str(i)) == 3, dict_demo.values()))
print('字典中长度为3的值是:', len_value[0])
5.4 遍历与删除
错误写法:
# 错误的做法
d = dict(a=1, b=2, c=3)
for k, v in d.items():print(d.pop(k))
# 抛出RuntimeError: dictionary changed size during iteration
在使用 keys()、values()、items() 等方法遍历的时候,不可以改变字典的 size,正确写法:
d = dict(a=1, b=2, c=3)
while len(d):print(d.popitem())
while d:print(d.popitem())
上面的 while 循环虽然可以移除字典元素,但是很少使用,不如直接 clear。for 循环正确删除:
d = dict(a=1, b=2, c=3)
keys = []
for k, v in d.items():keys.append(k)
for k in keys:d.pop(k)
print(d)
集合 set 在遍历中,也不能改变其长度。
5.5 字典视图
dict 的实例方法 .keys()、.values() 和 .items() 分别返回 dict_keys、dict_values 和 dict_items 类的实例。这些字典视图是 dict 内部实现使用的数据结构的只读投影。Python 2 中对应的方法返回列表,重复 dict 中已有的数据,有一定的内存开销。另外,视图还取代了返回迭代器的旧方法。下面的示例展示所有字典视图均支持的一些基本操作。示例 .values() 方法返回 dict 对象的值视图:
In [9]: d = dict(a=10,b=20,c=30)In [10]: values = d.values()In [11]: values
Out[11]: dict_values([10, 20, 30])In [12]: len(values)
Out[12]: 3In [13]: list(values)
Out[13]: [10, 20, 30]In [14]: reversed(values)
Out[14]: <dict_reversevalueiterator at 0x2559fd935b0>In [15]: values[0]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[15], line 1
----> 1 values[0]TypeError: 'dict_values' object is not subscriptable
通过视图对象的字符串表示形式查看视图的内容。可以查询视图的长度。视图是可迭代对象,方便构建列表。视图实现了 __reversed__
方法,返回一个自定义迭代器。不能使用 []
获取视图中的项。视图对象是动态代理。更新原 dict 对象后,现有视图立即就能看到变化。续上面的示例:
In [16]: d
Out[16]: {'a': 10, 'b': 20, 'c': 30}In [17]: d['z'] = 99In [18]: values
Out[18]: dict_values([10, 20, 30, 99])In [19]: values
Out[19]: dict_values([10, 20, 30, 99])In [20]: d['x'] = 88In [21]: values
Out[21]: dict_values([10, 20, 30, 99, 88])
dict_keys、dict_values 和 dict_items 是内部类,不能通过 __builtins__
或标准库中的任何模块获取,尽管可以得到实例,但是在 Python 代码中不能自己动手创建。
In [22]: values_class = type({}.values())In [23]: v = values_class()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[23], line 1
----> 1 v = values_class()TypeError: cannot create 'dict_values' instances
dict_values 类是最简单的字典视图,只实现了 __len__
、__iter__
和 __reversed__
这 3 个特殊方法。除此之外,dict_keys 和 dict_items 还实现了多个集合方法,基本与 frozenset 类相当。需要特别注意的是,dict_keys 和 dict_items 实现了一些特殊方法,支持强大的集合运算符,包括 &(交集)、|(并集)、-(差集)和 ^(对称差集)。例如,使用 & 运算符可以轻易获取两个字典都有的键。
In [24]: d1 = dict(a=1, b=2, c=3, d=4)In [25]: d2 = dict(b=20, d=40, e=50)In [26]: d1.keys() & d2.keys()
Out[26]: {'b', 'd'}
注意,& 运算符返回一个 set。更方便的是,字典视图的集合运算符均兼容 set 实例,如下所示:
In [29]: d1
Out[29]: {'a': 1, 'b': 2, 'c': 3, 'd': 4}In [30]: s = {'a', 'e', 'i'}In [31]: d1.keys() | s
Out[31]: {'a', 'b', 'c', 'd', 'e', 'i'}
仅当 dict 中的所有值均可哈希时,dict_items 视图才可当作集合使用。倘若 dict 中有不可哈希的值,对 dict_items 视图做集合运算将抛出 TypeError: unhashable type 'T'
,其中 T 是不可哈希的类型。相反,dict_keys 视图始终可当作集合使用,因为按照其设计,所有键均可哈希。使用集合运算符处理视图可以省去大量循环和条件判断。繁重工作都可交给 C 语言实现的 Python 高效完成。
六、字典的key
6.1 “可哈希”指什么
字典的 key 和 set 的元素要求一致:
- set 的元素可以就是看做 key,set 可以看做 dict 的简化版
- hashable 可哈希才可以作为 key,可以使用 hash() 函数进行测试
- 使用 key 访问,就如同列表使用 index 访问一样,时间复杂度都是
O(1)
,这也是最好的访问元素的方式
示例代码:
d = {1: 0,2.0: 3,"abc": None,('hello', 'world', 'python'): "string",b'abc': '135'
}
print(d)
可哈希
在 Python 术语表中有定义,摘录(有部分改动)如下:如果一个对象的哈希码在整个生命周期内永不改变(依托 __hash__()
方法),而且可与其他对象比较(依托 __eq__()
方法),那么这个对象就是可哈希的。两个可哈希对象仅当哈希码相同时相等。数值类型以及不可变的扁平类型 str 和 bytes 均是可哈希的。如果容器类型是不可变的,而且所含的对象全是可哈希的,那么容器类型自身也是可哈希的。frozenset 对象全部是可哈希的,因为按照定义,每一个元素都必须是可哈希的。仅当所有项均可哈希,tuple 对象才是可哈希的。请考察以下示例中的 tt、tl 和 tf。
C:\Users\amoxiang>ipython
Python 3.10.11 (tags/v3.10.11:7d4cc5a, Apr 5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.26.0 -- An enhanced Interactive Python. Type '?' for help.In [1]: tt = (1, 2, (30, 40))In [2]: hash(tt)
Out[2]: -3907003130834322577In [3]: tl = (1, 2, [30, 40])
In [5]: hash(tl)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[5], line 1
----> 1 hash(tl)TypeError: unhashable type: 'list'In [6]: tf = (1, 2, frozenset([30, 40]))In [7]: hash(tf)
Out[7]: 5149391500123939311
一个对象的哈希码根据所用的 Python 版本和设备架构有所不同。如果出于安全考量而在哈希计算过程中 加盐,那么哈希码也会发生变化。 正确实现的对象,其哈希码在一个 Python 进程内保持不变。默认情况下,用户定义的类型是可哈希的,因为自定义类型的哈希码取自 id(),而且继承自 object 类的 __eq__()
方法只不过是比较对象 ID。如果自己实现了 __eq__()
方法,根据对象的内部状态进行比较,那么仅当 __hash__()
方法始终返回同一个哈希码时,对象才是可哈希的。实践中,这要求 __eq__()
和 __hash__()
只考虑在对象的生命周期内始终不变的实例属性。
6.2 哈希表(散列表)是什么?
6.2.1 哈希表的构建
在初中的数学课本中学习过函数的相关知识,给定一个 x,通过一个数学公式,只需要将 x 的值带入公式就可以求出一个新的值 y。哈希表的建立同函数类似,把函数中的 x 用查找记录时使用的关键字来代替,然后将关键字的值带入一个精心设计的公式中,就可以求出一个值,用这个值来表示记录存储的 哈希地址。即:
数据的哈希地址=f(关键字的值)
哈希地址只是表示在查找表中的存储位置,而不是实际的物理存储位置。f() 是一个函数,通过这个函数可以快速求出该关键字对应的的数据的哈希地址,称之为 "哈希函数"
。 例如,这里有一个电话簿(查找表),电话簿中有 4 个人的联系方式:
张三 13912345678
李四 15823457890
王五 13409872338
赵六 13805834722
假如想查找李四的电话号码,对于一般的查找方式最先想到的是从头遍历,一一比较。而如果将电话簿构建成一张哈希表,可以直接通过名字 "李四"
直接找到电话号码在表中的位置。在构建哈希表时,最重要的是哈希函数的设计。例如设计电话簿案例中的哈希函数为:每个名字的姓的首字母的 ASCII 值即为对应的电话号码的存储位置。这时会发现,张三和赵六两个关键字的姓的首字母都是 Z ,最终求出的电话号码的存储位置相同,这种现象称为 冲突。 在设计哈希函数时,要尽量地避免冲突现象的发生。对于哈希表而言,冲突只能尽可能地少,无法完全避免。
6.2.2 哈希函数的构造
常用的哈希函数的构造方法有 6 种:直接定址法、数字分析法、平方取中法、折叠法、除留余数法和随机数法。
直接定址法: 其哈希函数为一次函数,即以下两种形式:
H(key)= key 或者 H(key)=a * key + b
其中 H(key)表示关键字为 key 对应的哈希地址,a 和 b 都为常数。
例如有一个从 1 岁到 100 岁的人口数字统计表,如下表所示:
假设其哈希函数为第一种形式,其关键字的值表示最终的存储位置。若需要查找年龄为 25 岁的人口数量,将年龄 25 带入哈希函数中,直接求得其对应的哈希地址为 25(求得的哈希地址表示该记录的位置在查找表的第 25 位)。
数字分析法: 如果关键字由多位字符或者数字组成,就可以考虑抽取其中的 2 位或者多位作为该关键字对应的哈希地址,在取法上尽量选择变化较多的位,避免冲突发生。例如下表中列举的是一部分关键字,每个关键字都是有 8 位十进制数组成:
通过分析关键字的构成,很明显可以看到关键字的第 1 位和第 2 位都是固定不变的,而第 3 位不是数字 3 就是 4,最后一位只可能取 2、7 和 5,只有中间的 4 位其取值近似随机,所以为了避免冲突,可以从 4 位中任意选取 2 位作为其哈希地址。
平方取中法 是对关键字做平方操作,取中间得几位作为哈希地址。此方法也是比较常用的构造哈希函数的方法。例如关键字序列为{421,423,436},对各个关键字进行平方后的结果为{177241,178929,190096},则可以取中间的两位 {72,89,00} 作为其哈希地址。
折叠法 是将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为哈希地址。此方法适合关键字位数较多的情况。例如,在图书馆中图书都是以一个 10 位的十进制数字为关键字进行编号的,若对其查找表建立哈希表时,就可以使用折叠法。若某书的编号为:0-442-20586-4,分割方式如下图中所示,在对其进行折叠时有两种方式:一种是移位折叠,另一种是间界折叠:移位折叠 是将分割后的每一小部分,按照其最低位进行对齐,然后相加;间界折叠 是从一端向另一端沿分割线来回折叠。
除留余数法: 若已知整个哈希表的最大长度 m,可以取一个不大于 m 的数 p,然后对该关键字 key 做取余运算,即:H(key)= key % p。在此方法中,对于 p 的取值非常重要,由经验得知 p 可以为不大于 m 的质数或者不包含小于 20 的质因数的合数。
随机数法: 是取关键字的一个随机函数值作为它的哈希地址,即:H(key)= random(key),此方法适用于关键字长度不等的情况。 注意:这里的随机函数其实是伪随机函数,随机函数是即使每次给定的 key 相同,但是 H(key)都是不同;而伪随机函数正好相反,每个 key 都对应的是固定的 H(key)。
如此多的构建哈希函数的方法,在选择的时候,需要根据实际的查找表的情况采取适当的方法。通常考虑的因素有以下几方面:
- 关键字的长度。如果长度不等,就选用随机数法。如果关键字位数较多,就选用折叠法或者数字分析法;反之如果位数较短,可以考虑平方取中法;
- 哈希表的大小。如果大小已知,可以选用除留余数法;
- 关键字的分布情况;
- 查找表的查找频率;
- 计算哈希函数所需的时间(包括硬件指令的因素)
6.2.3 处理冲突的方法
对于哈希表的建立,需要选取合适的哈希函数,但是对于无法避免的冲突,需要采取适当的措施去处理。通常用的处理冲突的方法有以下几种:
- 开放定址法 H(key)=(H(key)+ d)MOD m(其中 m 为哈希表的表长,d 为一个增量) 当得出的哈希地址产生冲突时,选取以下 3 种方法中的一种获取 d 的值,然后继续计算,直到计算出的哈希地址不在冲突为止,这 3 种方法为:
- 线性探测法:d=1,2,3,…,m-1
- 二次探测法:d=12,-12,22,-22,32,…
- 伪随机数探测法:d=伪随机数
- 例如,在长度为 11 的哈希表中已填写好 17、60 和 29 这 3 个数据(如图 (a) 所示),其中采用的哈希函数为:H(key)=key MOD 11,现有第 4 个数据 38 ,当通过哈希函数求得的哈希地址为 5,与 60 冲突,则分别采用以上 3 种方式求得插入位置如图(b)所示:
注释:在线性探测法中,当遇到冲突时,从发生冲突位置起,每次 +1,向右探测,直到有空闲的位置为止;二次探测法中,从发生冲突的位置起,按照 +12,-12,+22,…如此探测,直到有空闲的位置;伪随机探测,每次加上一个随机数,直到探测到空闲位置结束。
- 再哈希法。当通过哈希函数求得的哈希地址同其他关键字产生冲突时,使用另一个哈希函数计算,直到冲突不再发生。
- 链地址法。将所有产生冲突的关键字所对应的数据全部存储在同一个线性链表中。例如有一组关键字为{19,14,23,01,68,20,84,27,55,11,10,79},其哈希函数为:H(key)=key MOD 13,使用链地址法所构建的哈希表如下图所示:
- 建立一个公共溢出区。建立两张表,一张为基本表,另一张为溢出表。基本表存储没有发生冲突的数据,当关键字由哈希函数生成的哈希地址产生冲突时,就将数据填入溢出表。
七、有序性
字典元素是按照 key 的 hash 值无序存储的。但是,有时候我们却需要一个有序的元素顺序,Python 3.6 之前,使用 OrderedDict 类可以做到,3.6开始 dict 自身支持。到底 Python 对一个无序数据结构记录了什么顺序?
(py35) C:\Users\amoxiang>python
Python 3.5.6 |Anaconda, Inc.| (default, Aug 26 2018, 16:05:27) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> d = {'a':300, 'b':200, 'c':100, 'd':50}
>>> d
{'a': 300, 'b': 200, 'c': 100, 'd': 50}
>>> d
{'a': 300, 'b': 200, 'c': 100, 'd': 50}
>>> d
{'a': 300, 'b': 200, 'c': 100, 'd': 50}
>>> list(d.keys())
['a', 'b', 'c', 'd']
>>>
>(py35) C:\Users\amoxiang>python
Python 3.5.6 |Anaconda, Inc.| (default, Aug 26 2018, 16:05:27) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> d = {'a':300, 'b':200, 'c':100, 'd':50}
>>> d
{'b': 200, 'c': 100, 'a': 300, 'd': 50}
>>> d
{'b': 200, 'c': 100, 'a': 300, 'd': 50}
>>>
Python 3.6 之前,在不同的机器上,甚至同一个程序分别运行2次,都不能确定不同的 key 的先后顺序。Python 3.6+ 表现如下:
C:\Users\amoxiang>python
Python 3.10.11 (tags/v3.10.11:7d4cc5a, Apr 5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> d = {'c': 100, 'a': 300, 'b': 200, 'd': 50}
>>> d
{'c': 100, 'a': 300, 'b': 200, 'd': 50}
>>> d.keys()
dict_keys(['c', 'a', 'b', 'd'])
C:\Users\amoxiang>python
Python 3.10.11 (tags/v3.10.11:7d4cc5a, Apr 5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> d = {'c': 100, 'a': 300, 'b': 200, 'd': 50}
>>> d
{'c': 100, 'a': 300, 'b': 200, 'd': 50}
Python 3.6+,记录了字典 key 的录入顺序,遍历的时候,就是按照这个顺序。如果使用 d = {'a':300, 'b':200, 'c':100, 'd':50}
,就会造成以为字典按照 key 排序的错觉。目前,建议不要 Python 3.6+ 提供的这种字典特性,还是认为字典返回的是无序的,可以在 Python 不同版本中考虑使用 OrderedDict 类来保证这种录入顺序。
collections.OrderedDict。自 Python 3.6 起,内置的 dict 也保留键的顺序。使用 OrderedDict 最主要的原因是编写与早期 Python 版本兼容的代码。不过,dict 和 OrderedDict 之间还有一些差异,Python 文档中有说明,摘录如下(根据日常使用频率,顺序有调整)。
- OrderedDict 的等值检查考虑顺序。
- OrderedDict 的 popitem() 方法签名不同,可通过一个可选参数指定移除哪一项。
- OrderedDict 多了一个 move_to_end() 方法,便于把元素的位置移到某一端。
- 常规的 dict 主要用于执行映射操作,插入顺序是次要的。
- OrderedDict 的目的是方便执行重新排序操作,空间利用率、迭代速度和更新操作的性能是次要的。
- 从算法上看,OrderedDict 处理频繁重新排序操作的效果比 dict 好,因此适合用于跟踪近期存取情况(例如在 LRU 缓存中)。
八、小结
Python 使用哈希表实现 dict,因此字典的效率非常高,不过这种设计对实践也有一些影响,不容忽视。
- 键必须是可哈希的对象。必须正确实现
__hash__
和__eq__
方法。 - 通过键访问项速度非常快。对于一个包含数百万个键的 dict 对象,Python 通过计算键的哈希码就可以直接定位键,然后找出索引在哈希表中的偏移量,稍微尝试几次就能找到匹配的条目,因此开销不大。
- 在 CPython 3.6 中,dict 的内存布局更为紧凑,顺带的一个副作用是键的顺序得以保留。Python 3.7 正式支持保留顺序。
- 尽管采用了新的紧凑布局,但是字典仍然占用大量内存,这是不可避免的。对容器来说,最紧凑的内部数据结构是指向项的指针的数组 (元组就是这样存储的)。 与之相比,哈希表中的条目存储的数据更多,而且为了保证效率,Python 至少需要把哈希表中三分之一的行留空。
- 为了节省内存,不要在
__init__
方法之外创建实例属性。最后一点背后的原因是,Python 默认在特殊的__dict__
属性中存储实例属性,而这个属性的值是一个字典,依附在各个实例上。 自从 Python 3.3 实现"PEP 412—Key-Sharing Dictionary"
之后,类的实例可以共用一个哈希表,随类一起存储。如果新实例与__init__
返回的第一个实例拥有相同的属性名称,那么新实例的__dict__
属性就共享这个哈希表,仅以指针数组的形式存储新实例的属性值。__init__
方法执行完毕后再添加实例属性,Python 就不得不为这个实例的__dict__
属性创建一个新哈希表(在 Python 3.3 之前,这是所有实例的默认行为)。根据 PEP 412,这种优化可将面向对象程序的内存使用量减少 10%~20%。
九、综合练习----序列的应用
1.列表对象的什么方法用于对列表进行逆序排列?
A.list()
B.append()
C.reverse()
D.zip()
正确答案是:C。reverse() 方法是用于将列表中的元素逆序排列,即将列表中的元素倒置。此方法是原地修改,不会返回新列表,而是直接修改原始列表。A.list() 用于将可迭代对象转换为列表,不是用来逆序的。B.append() 用于向列表末尾添加元素,不涉及逆序操作。D.zip() 用于将多个可迭代对象 "压缩"
成一个元组的可迭代对象,也不涉及逆序操作。
2.创建元组时,可以使用什么赋值运算符直接将一个元组赋值给变量?
A.==
B.:
C.—
D.=
正确答案是:D。在 Python 中,创建元组时,可以使用 = 运算符将元组赋值给一个变量。例如:my_tuple = (1, 2, 3)。A.== 是用来进行比较的运算符,而不是赋值运算符。B.: 冒号用于切片操作,或者在字典、函数定义中使用,不是赋值运算符。C.—这个符号没有在 Python 中用于赋值。
3.检查元素是否为序列成员所用的关键字是:
A.out
B.not
C.in
D.for
正确答案是:C。在 Python 中,in 关键字用于检查某个元素是否是某个序列(如列表、元组、字符串等)的成员。它返回一个布尔值(True 或 False),表示元素是否在序列中。A.out 不是一个有效的关键字,在 Python 中没有这样的用法。B.not 是一个逻辑运算符,用于进行布尔运算,表示 "非"
,不是用来检查成员的。D.for 用于迭代序列中的元素,不用于检查是否是序列的成员。
4.如果我们想要将序列转换为列表,需要用到什么函数?
A.str()
B.num()
C.first()
D.list()
正确答案是:D。list() 函数可以将其他可迭代的序列(如元组、字符串、集合等)转换为一个列表。A.str() 是将对象转换为字符串类型的函数,不用于转换为列表。B.num() 不是一个 Python 中的有效函数。C. first() 也不是一个 Python 中的有效函数。
5.如果想要将一个列表中的全部元素添加到另一个列表中,可以使用列表对象的什么方法实现?
A.append()
B.insert()
C.extend()
D.reversed()
正确答案是:C。extend() 方法用于将一个列表中的所有元素添加到另一个列表的末尾。它会将另一个列表的元素展开并添加到当前列表。A.append() 方法将一个单独的元素添加到列表的末尾,而不是将另一个列表的所有元素添加到当前列表。B.insert() 方法用于在指定位置插入一个元素,而不是将一个列表的所有元素添加到另一个列表。D.reversed() 返回一个反转顺序的迭代器,不会改变原列表,也不用于添加元素。
6.通过()可以访问列表中的任何元素?
A.查询
B.切片
C.索引
D.计算
正确答案是:C。索引(index)是通过括号 [] 来访问列表中的元素。通过指定索引值,可以访问列表中的任何单个元素。A. 查询一般是指通过某些条件来查找元素,而不是直接通过索引访问元素。B.切片是使用 : 运算符来访问列表中的一部分(子列表),而不是单个元素(一般来说,ps:切片也可以访问单个元素)。D.计算与访问元素无关,主要用于进行数学运算。
7.下面关于元组的说法正确的是?
A.在进行元组连接时,连接的内容不限制
B.元组可以和列表进行连接
C.如果要连接的元组只有一个元素,那么不需要逗号
D.元组是不可变序列,不能对它的单个元素值进行修改
正确答案是:D。元组是不可变的序列,一旦创建,就不能修改其内容,包括不能修改其单个元素的值。如果尝试修改元组中的元素,将会引发错误。A.连接的内容是有限制的,元组连接时必须确保连接的两个对象都是元组类型。如果将元组与其他类型连接,必须先将其转换为元组。B.元组和列表是不同的类型,不能直接使用 + 运算符连接它们。如果要连接,可以先将列表转换为元组,或者将元组转换为列表。C.只有一个元素的元组需要使用逗号来区分,避免与括号表达式混淆。补充: 元组本身是不可变的,即不能修改元组中的元素(如修改元组中的 1、2、3)。元组中包含的可变对象(如列表、字典等)是可以修改的,因为可变对象本身并不会受到元组的不可变性的限制。
8.不能对元组进行以下哪项操作:
A.截取元组中的元素
B.连接多个元组
C.修改元组中的元素值
D.删除元组
正确答案是:C。A.截取元组中的元素:元组是可以截取(切片)的,就像列表一样。通过切片操作,可以提取元组中的一部分元素。B.连接多个元组:元组可以通过加法(+)连接多个元组。C.修改元组中的元素值:元组的不可变性是指你不能修改元组中的元素。试图修改元组中的元素会导致错误。D.删除元组:虽然元组本身是不可变的,但你可以通过 del 删除整个元组(而不是修改其中的元素)。
9.列表对象的()方法用来删除首次出现的指定元素:
A.pop()
B.del
C.remove()
D.clear()
正确答案是:C。A.pop():pop() 方法用于删除并返回列表中的一个元素,默认删除最后一个元素。也可以传入索引删除指定位置的元素。它删除的是元素,而不是值。B.del:del 是一个语句,可以删除指定索引位置的元素或整个列表。它不仅限于列表,也可以用于删除其他对象。C.remove():remove() 方法用来删除列表中首次出现的指定元素。如果指定元素不存在,会引发 ValueError。D.clear():clear() 方法会删除列表中的所有元素,清空列表。
10.使用for循环和什么函数可以实现同时输出索引值和元素内容?
A.sum()
B.item()
C.list()
D.enumerate()
正确答案是:D。A.sum():sum() 函数用于计算可迭代对象的所有元素的和,与索引无关。B.item():item() 不是Python内置函数,用于获取单个元素的值不是用来处理索引的。C.list():list() 是用于将其他可迭代对象转换成列表的函数,并不涉及索引的处理。D.enumerate():enumerate() 函数用于将一个可迭代对象(如列表)转化为一个索引值和元素内容的组合,返回一个 enumerate 对象,可以用于同时输出元素的索引和内容。
11.下面的代码运行后,结果是什么?
tuple2 = ('5', '4', '8')
print(min(tuple2))A.5
B.4
C.8
D.3
正确答案是:B。min() 函数用于返回可迭代对象中的最小值。当应用于元组时,它会按照元素的字典序(即按字符串的排序顺序)来确定最小值。按照字典序,字符串 '4'
小于 '5'
和 '8'
,因此 min(tuple2) 返回 '4'
。字典序是按字符的顺序进行排序的一种规则,通常基于每个字符的 Unicode 或 ASCII 值来比较字符的大小。
12.小明今年17岁,爸爸41岁,妈妈40岁,爷爷今年60岁,小明写了一段代码,看看输出什么吧。
nj = [17, 40, 41, 60]
print(min(nj))A.17
B.41
C.40
D.60
正确答案是:A。min() 函数用于返回列表中最小的元素。由于在这个列表中,数字 17 是最小的,所以输出结果为:A.17
13.小明每天吃饭都是剩很多就丢掉了,于是小红给他写了一段代码,用来警示小明,快来看看输出什么吧。
verse = ["锄禾日当午", "汗滴禾下土", "谁知盘中餐", "粒粒皆辛苦"]
print(verse[3])A.锄禾日当午
B.汗滴禾下土
C.谁知盘中餐
D.粒粒皆辛苦
正确答案是:D。print(verse[3]) 语句会输出列表 verse 中索引为 3 的元素。由于 Python 中列表的索引从 0 开始,索引 3 对应的是列表中的最后一个元素,即:D.粒粒皆辛苦。
14.理财第一步,从记账开始。周日晚上小明整理了自己从周一到周日这7天的消费情况,分别为23,58,44,69,124,40,60,但在他记完账之后,晚上睡前刷朋友圈时又在众筹平台上捐款100元,下列哪项不能够计算他本周末一共花了多少钱:
A.expense = [23, 58, 44, 69, 124, 40, 60]expense.insert(7, 100)print(sum(expense[0:8]))B.expense = [23, 58, 44, 69, 124, 40, 60]expense[6] = 160print(sum(expense[0:7]))C.expense = [23, 58, 44, 69, 124, 40, 60]expense.append(100)print(sum(expense[0:8]))D.expense = [23, 58, 44, 69, 124, 40, 60]expense.extend(100)print(sum(expense[0:8]))
正确答案是:D。extend(100) 是错误的,因为 extend() 方法需要传入一个可迭代对象(如列表或元组)。而 100 是一个整数,不是可迭代对象。因此,这段代码会报错,即不能正确计算总支出。A.选项:insert(7, 100) 方法在列表的第7个索引位置插入100,这样列表会变成 [23, 58, 44, 69, 124, 40, 60, 100]。然后使用 sum(expense[0:8]) 来计算总和,结果是正确的。B.选项:在这个代码中,expense[6] = 160 会将列表中的第六个元素(也就是 60)替换为 160。然后,使用 sum(expense[0:7]) 计算从周一到周日的支出总和。此时,列表变成了 [23, 58, 44, 69, 124, 40, 160],这已经是修改后的列表,包含了捐款的100元(通过修改原有的60元为160元)。所以,B 选项通过替换60为160,实际上是包含了100元的捐款,结果是正确的。C.选项:append(100) 将100添加到列表的末尾,列表变成 [23, 58, 44, 69, 124, 40, 60, 100]。然后使用 sum(expense[0:8]) 计算总和,结果正确。
15.想要向x = {1,2,3} 中添加元素4,正确写法为:
A.x.add(4)
B.x.append(4)
C.x.insert(4)
D.x.update(4)
正确答案是:A。B.append(4):错误,因为 append() 是列表的方法。C.insert(4):错误,insert() 是列表的方法。D.update(4):虽然 update() 可用于集合,但它的参数应该是可迭代对象,而不是一个单独的元素。
16.小明问小红最喜欢的足球明星是谁,小红写了一段代码告诉他,你能看出是谁么?
sdqx =['C罗','梅西','内马尔']
print(sdqx[0:1])A.C罗
B.梅西
C.内马尔
D.['C罗']
正确答案是:D。sdqx[0:1] 的切片操作会返回一个列表,而不是单独的字符串。所以 sdqx[0:1] 的结果是 ['C罗']
,而不是 "C罗"
。简单来说,A选项是一个字符串,而D选项是一个列表,D选项才是代码实际输出的结果。
17.曾几何时大家开玩笑时总会形容对方很2,但也有可能对方真的很2~计算元素2在列表中出现的次数,print语句中应填入:
m = [1,2,2,3,3,3,4,4,4,4]
print()A.m.count(2)
B.m.index(2)
C.m.count(1,2)
D.m.index(1,2)
正确答案是:A。A.m.count(2):该方法 count() 用于计算指定元素在列表中出现的次数。所以这行代码正确地返回元素 2 出现的次数。结果是 2。B.m.index(2):该方法 index() 用于返回指定元素在列表中的第一个匹配项的索引。如果元素不存在,会抛出错误。它并不会返回元素出现的次数。C.m.count(1, 2):这是错误的写法。count() 只接受一个参数,用来计数指定元素的出现次数,不能传递两个参数。D.m.index(1, 2):同样是错误的,index() 方法接受一个元素和可选的开始索引、结束索引,目的是找到元素的位置,而不是计数。
18.你懂元组的特别吗?想要通过下列代码访问元组元素,将会输出:
temp = (1,2,3)
print(temp[2:])A.3
B.(3,)
C.(3)
D.(2,3)
正确答案是:B。temp[2:] 的结果是一个切片,表示从索引 2 开始(即元素 3)直到元组的末尾。由于切片操作返回的是一个元组,所以结果是 (3,),即包含一个元素 3 的元组。
19.NBA的新赛季马上要开始了,宇宙无敌勇士队这个赛季又引进了超级中锋考辛斯,下面这组是勇士队5个首发名单,看看最后输出的是谁吧!
ysd=['库里','克莱','杜兰特','格林','考辛斯']
print(ysd[0:5:2])A.['库里', '杜兰特', '格林']
B.['克莱', '杜兰特', '考辛斯']
C.['库里', '格林', '考辛斯']
D.['库里', '杜兰特', '考辛斯']
正确答案是:D。ysd 是一个列表,包含勇士队的5个首发球员:['库里', '克莱', '杜兰特', '格林', '考辛斯']
。ysd[0:5:2] 是一个切片操作,含义如下:0:5 表示从索引0开始,到索引4(包含)结束,即整个列表。2 表示步长为2,也就是每隔一个元素选择一个。从列表的第0个索引开始,步长为2,所以会选取索引位置为 0、2 和 4 的元素。ysd[0] 是 '库里'
,ysd[2] 是 '杜兰特'
,ysd[4] 是 '考辛斯'
。因此,输出的结果是 ['库里', '杜兰特', '考辛斯']
。
20.这列表m被n“蹭”一下子之后,还是原来的m吗?下面代码的输出结果为:
m = [1,2]
n = m
n.append(3)
print(m)A.[1,2]
B.1,2
C.[1,2,3]
D.1,2,3
正确答案是:C。m = [1, 2]:定义了列表 m,包含两个元素 [1, 2]。n = m:这条语句使得变量 n 和 m 指向同一个列表对象,即它们是同一个列表,修改 n 实际上也会修改 m。n.append(3):向 n 列表中追加元素 3,由于 n 和 m 是指向同一个列表,所以 m 也会被修改。print(m):打印 m,它已经变成了 [1, 2, 3]。结果:列表 m 和 n 共享同一个内存地址,因此 n.append(3) 使得 m 的内容也被改变,变成了 [1, 2, 3]。
21.最新一期的好声音李健已经吸粉无数,小明就是其中一个,小明写了一段代码想要输出李健,却一直不对,你能帮他改一下么?
verse = ["李健","哈林","周杰伦","谢霆锋"]
print(verse[1])A.不能
B.第一行谢霆锋改成李健
C.第二行1改成0
D.第二行1改成2
正确答案是:C。
22.运动会上百米比赛的成绩前三名是10.9秒,11.3秒,11.5秒,小明想进行降序排序,看看他写的有没有错误吧。
bmcj = [10.9,11.3,11.5]
bmcj.sort()
print(bmcj)A.2行最后加:
B.2行()里面加reverse=True
C.3行bmcj改成sort
D.没有错误
正确答案是:B。bmcj.sort() 默认按升序排序,如果想要降序排序,应该在 sort() 方法中传入参数 reverse=True。sort() 是原地排序,会直接修改列表 bmcj,不返回新的列表。
23.小黑组有5个人,期末考试Python成绩分别为93,89,96,87,99,90分以上为优秀,小黑想输出优秀的成绩,看看他写的对吗?
cj =[93,89,96,87,99]
dcj = [x for x in cj if x>90]
print('成绩优秀的:',dcj)A.2行for改成at
B.2行in改成as
C.3行去掉,
D.太棒啦,没有错误
正确答案是:D。
24.作为史上第一个5个首发全明星球队,勇士队受到了许多关注,小明写了一段代码,创建一个勇士队之前的全明星列表,在创建一个新加入的考辛斯的列表,把考辛斯加入到勇士队伍里,看看他有什么错误吧。
oldlist = ['库里','杜兰特','克莱','格林',]
newlist = ['考辛斯']
oldlist.join(newlist)
print(oldlist)A.第四行最后加:
B.'格林',后面,去掉
C.第三行join改成extend
D.写的全对,棒棒哒!
正确答案是:C。.join() 方法:join() 是字符串的方法,用于将多个字符串连接在一起,但它不能直接用于列表。对于列表,应该使用 extend() 方法将一个列表的元素添加到另一个列表中。
25.可以通过()从字典中获取指定项:
A.键
B.值
C.索引
D.以上都可以
正确答案是:A。字典是由键和值对组成的。可以通过指定键来获取对应的值。
26.经典小学语文题,用“既……又……”造句。请准确的补充这个句子:既是无序序列又不可重复的是():
A.列表
B.元组
C.字典
D.集合
正确答案是:D。集合(set)是一个无序且不可重复的数据结构,符合 “既是无序序列又不可重复”的描述。列表(list)和元组(tuple)是有序的,并且可以包含重复元素。字典(dict)虽然是无序的,但它由键值对组成,且键是唯一的,不完全符合题意。
27.在Python中“{}”表示的是:
A.空集合
B.空字典
C.空元组
D.空列表
正确答案是:B。在Python中,{} 默认表示一个空字典,而不是空集合。空集合使用set()来表示,因为{}会被解释为字典。空元组使用()来表示。空列表使用[]来表示。
28.下列哪个符号是集合的并集运算符:
A.&
B.|
C.-
D.^
正确答案是:B。在Python中,集合的并集运算符是竖线符号|。&是集合的交集运算符。-是集合的差集运算符。^是集合的对称差运算符。
29.做人还是要有原则的,不能因为我长得漂亮就说啥都对~关于字典,下列说法不正确的是:
A.字典的键必须是唯一的
B.字典的值可以是任何数据类型
C.字典的值可以重复
D.字典的键是可变的
正确答案是:D。A.字典的键必须是唯一的:正确,字典中的每个键都是唯一的。B.字典的值可以是任何数据类型:正确,字典的值可以是任何数据类型,包括数字、字符串、列表、甚至字典。C.字典的值可以重复:正确,字典的值是可以重复的,不要求唯一。D.字典的键是可变的:错误,字典的键必须是不可变类型,比如字符串、数字和元组。可变类型(如列表)不能作为字典的键。
30.2018年1月28日,36岁的费德勒获得了自己第20个大满贯,成为网球史上的第一人。截止到2018年澳网比赛,现役队员中获得过大满贯的运动员有:费德勒,20次;纳达尔,16次;德约克维奇,12次。想要输出费德勒获得大满贯的次数,第4行应填入代码:
person = ('费德勒','纳达尔','德约克维奇')
times = ['20次','16次','12次']
dictionary = dict(zip(person,times))A.print(dictionary = "费德勒")
B.print(dictionary["费德勒"])
C.print(dictionary[0])
D.print(dictionary("费德勒"))
正确答案是:B。
31.下列代码的输出结果为:
s = {1:30 , 2:20 , 3:10}
print(sorted(s.values()))A.[1, 2, 3]
B.[10, 20, 30]
C.{3:10, 2:20, 1:30}
D.{3, 2, 1}
正确答案是:B。s.values() 会返回字典 s 中所有的值,即 [30, 20, 10]。sorted(s.values()) 对这些值进行排序,返回的是一个按升序排列的列表 [10, 20, 30]。A、C、和 D 都不是 sorted(s.values()) 的结果,因为它们分别表示了错误的数据结构或排序。
32.小明很好奇字典可以求和吗?于是写下以下代码,输出结果将会是:
x = {0:1 , 2:3 , 4:5}
print(sum(x))A.9
B.6
C.3
D.运行报错
正确答案是:B。sum(x) 默认会对字典 x 的键进行求和,而不是对值进行求和。字典 x 的键是 {0, 2, 4},所以 sum(x) 就是对这些键求和:0 + 2 + 4 = 6。
33.表达式 {'a':1,'b':2,'c':3}.get('d',4)
的值为:
A.False
B.{'d':4}
C.{'a':1,'b':2,'c':3,'d':4}
D.4
正确答案是:D。.get() 方法会尝试从字典中获取指定键的值。如果字典中没有该键,则返回指定的默认值(此处是 4)。在这个例子中,字典中并没有 'd'
键,所以 get('d', 4)
会返回默认值 4。
34.小明有3本书,分别是《楚辞》,《悲惨世界》,《查令十字街》,小红有2本书:《飘》和《查令十字街》,她俩决定将共有的书捐出去一份,想要输出捐出去的书,输出语句应为:
m = {"《楚辞》","《悲惨世界》","《查令十字街》"}
h = {"《飘》","《查令十字街》"}A.print(m & h)
B.print(m | h)
C.print(m - h)
D.print(m ^ h )
正确答案是:A。要找出小明和小红共有的书,可以使用集合的交集运算符 &。m & h 表示取集合 m 和集合 h 的交集,即找出两者共同的元素。m | h 表示取集合 m 和集合 h 的并集,即合并两个集合中的所有元素。m - h 表示从集合 m 中移除所有在集合 h 中的元素,返回差集。m ^ h 表示取集合 m 和集合 h 的对称差集,即不在两个集合中都存在的元素。
35.小明周末又去吃披萨,店里有选择披萨尺寸的建议,小明想通过字典的形式输出这份食用建议,他写的对吗:
# 9寸比萨饼、建议1-2人食用
# 12寸比萨饼、建议2-3人食用
# 14寸比萨饼、建议3-4人食用size = ["9寸","12寸","14寸"]
person = ["1-2人食用","2-3人食用","3-4人食用"]
dictionary = dict(size,person)
print(dictionary)A.正确
B.不正确,应该将第1,2行的[]改为()
C.不正确,应该将第3行改为dictionary = dict(zip(size,person))
D.不正确,应该将第4行改为dictionary = dict.fromkeys(size)
正确答案是:C。
36.模拟手机通讯录编写一段代码,设置有查询、添加、删除联系人功能,小明写了如下代码,是否有误:
print('欢迎进入微信通讯录')
print('1.查询联系人')
print('2.添加联系人')
print('3.删除联系人')
print('4.退出通讯录')
contact={'小王':135,'小李':138,'小张':187,'小赵':155}
while True:temp=int(input("请输入你的选择:"))if temp==1:name=input("请输入要查询的姓名:")if name in contact.keys():print(contact.values())else:print("该联系人不在通讯录中")if temp==2:name=input("请输入要添加的联系人姓名:")number=input("请输入联系人电话:")contact[name]=numberif temp==3:name=input("请输入要删除的联系人姓名:")if name in contact.keys():contact.pop(name)else:print("该联系人不在通讯录中")if temp==4:breakA.第12行错误:应改为print(contact[name])
B.第21行错误,应改为if name not in contact.keys()
C.第18行错误,应改为contact.add(name:number)
D.第22行错误,应改为del contact[name]
正确答案是:A。
37.公司门口有一条狗,名字叫明明,今年7岁了,小黑想把它的信息输出出来,你看看哪里有错误吗?
dog = {'Name': '明明', 'Age': 7, 'Class': 'First'}
print ("名字: ", ['Name'])
print ("年龄: ", dog['Age'])A.1行明明后的,改成;
B.2行['Name']改成dog['Name']
C.3行dog['Age']改成['Age']
D.没有错误
正确答案是:B。
38.世界那么大,你想去看看?小明是想吃吃看!他想设计个小程序,输入国家名字后,自动弹出这个国家的国菜,他的梦想就是把这些菜都吃遍!当然前提是他要先学好编程,找好工作,攒好钱~ 先来看看他写的代码对不对吧:
food = {"日本": "生鱼片", "法国": "鹅肝", "泰国": "冬阴功汤","乌干达": "香蕉饭", "奥地利": "维也纳炸肉排"}
nation = input("请输入感兴趣的国家:(日本,法国,泰国,乌干达,奥地利)\n")
for item in food.keys():if nation == item:print(nation, "的代表菜是:", food.values())A.正确
B.不正确,第4行应改为 for item in food.items()
C.不正确,第5行应改为if nation == item:
D.不正确,第6行应改为print(nation,"的代表菜是:", food.get(item))
正确答案是:D。
至此今天的学习就到此结束了,笔者在这里声明,笔者写文章只是为了学习交流,以及让更多学习Python语言的读者少走一些弯路,节省时间,并不用做其他用途,如有侵权,联系博主删除即可。感谢您阅读本篇博文,希望本文能成为您编程路上的领航者。祝您阅读愉快!
好书不厌读百回,熟读课思子自知。而我想要成为全场最靓的仔,就必须坚持通过学习来获取更多知识,用知识改变命运,用博客见证成长,用行动证明我在努力。
如果我的博客对你有帮助、如果你喜欢我的博客内容,请点赞
、评论
、收藏
一键三连哦!听说点赞的人运气不会太差,每一天都会元气满满呦!如果实在要白嫖的话,那祝你开心每一天,欢迎常来我博客看看。
编码不易,大家的支持就是我坚持下去的动力。点赞后不要忘了关注
我哦!