5.内建数据结构
1 内建常用数据结构
- 序列(sequence)
- 字符串
str、字节序列bytes、bytearray - 列表
list、元组tuple
- 字符串
- 键值对(map)
- 集合
set、字典dict
- 集合
序列是一种数据存储方式,用来存储一系列的数据。在内存中,序列就是一块用来存放多个值的连续的内存空间。比如一个整数序列 [10, 20, 30, 40],可以这样示意表示:

由于 Python3 中一切皆对象,在内存中实际是按照如下方式存储的:
a = [10, 20, 30, 40]

从图示中,我们可以看出序列中存储的是整数对象的 * 地址*,而不是整数对象的值。
- Python 中常用的序列结构有:字符串、列表、元组。
- Python 中常用的键值对结构有:字典、集合。
上面学习的字符串就是一种序列。关于字符串里面很多操作,在序列中仍然会用到。
2 列表
2.1 为什么需要列表
列表:用于存储任意数目、任意类型的数据集合。
列表的实现是一个顺序表,类似于 Java 或其它语言中的数组。
列表是内置可变序列,是包含多个元素的有序连续的内存空间。列表定义的标准语法格式:
列表序列名称 = [列表中的元素1, 列表中的元素2, 列表中的元素3, ...]
案例演示:定义一个列表,用于保存苹果、香蕉以及菠萝。
list1 = ['apple', 'banana', 'pineapple']
# list 列表类型支持直接打印
print(list1)
# 打印列表的数据类型
print(type(list1))
字符串和列表都是序列类型,一个字符串是一个字符序列,一个列表是任何元素的序列。前面学习的很多字符串的方法,在列表中也有类似的用法,几乎一模一样。
列表:
- 一个排列整齐的队伍,Python:采用顺序表实现
- 列表内的个体称作元素,由若干元素组成列表
- 元素可以是任意对象(数字、字符串、对象、列表等)
- 列表内元素有顺序,可以使用索引
- 线性的数据结构
- 使用
[]表示 - 列表是可变的
基本语法 [] 创建
a = [] # 创建一个空的列表对象
a = [10, 20, 'ym', 'fbb']
list() 创建
使用 list() 可以将任何可迭代的数据转化成列表。
#%%
# 创建一个空的列表对象
a = list()
a
#%%
# 字符串转列表
a = list("gaoqi,sxt")
a
#%%
# range 转列表
a = list(range(10))
a

range() 创建整数列表
range() 可以帮助我们非常方便的创建整数列表,这在开发中及其有用。语法格式为:
range([start,] end [,step])
典型示例如下:
#%%
# range() 返回一个range对象
type(range(1, 10))
#%%
list(range(3, 15, 2))
#%%
list(range(15, 3, -1))
#%%
list(range(3, -10, -1))

推导式生成列表
简介一下,重点在 for 循环后讲。
使用列表推导式可以非常方便的创建列表,在开发中经常使用。但是,由于涉及到 for 循环和 if 语句。在此,仅做基本介绍。在我们控制语句后面,会详细讲解更多列表推导式的细节。
#%%
#循环创建多个元素
a = [x * 2 for x in range(5)]
a
#%%
# 通过 if 过滤元素
a = [x * 2 for x in range(100) if x % 9 == 0]
a

2.3 列表的相关操作
列表的作用是一次性存储多个数据,程序员可以对这些数据进行的操作有:增、删、改、查。

☆ 查操作
列表在计算机中的底层存储形式,列表和字符串一样,在计算机内存中都占用一段连续的内存地址,我们向访问列表中的每个元素,都可以通过索引下标的方式进行获取。

如果我们想获取列表中的某个元素,非常简单,直接使用索引下标:
list1 = ['apple', 'banana', 'pineapple']
# 获取列表中的 banana
print(list1[1])

查操作的相关方法:
| 函数 | 作用 |
|---|---|
index() |
指定数据所在位置的下标,如果列表中没有指定数据,则报异常 |
count() |
统计指定数据在当前列表中出现的次数,如果列表中不存在指定元素,返回 0 |
in |
判断指定数据在某个列表序列,如果在返回 True,否则返回 False |
not in |
判断指定数据不在某个列表序列,如果不在返回 True,否则返回 False |
len() |
返回列表长度,即列表中包含元素的个数 |
☆ index()
index() 可以获取指定元素首次出现的索引位置。语法是:
index(value [,start [,end]])
start 和 end 指定了搜索的范围。
#%%
# 1、查找某个元素在列表中出现的位置(索引下标)
list1 = ['apple', 'banana', 'pineapple']
print(list1.index('apple')) # 0
print(list1.index('peach')) # 报错
#%%
a = [10, 20, 30, 40, 50, 20, 30, 20, 30]
# 从头开始搜索列表中的第一个 20
print(a.index(20))
# 从索引位置 3 开始往后搜索的第一个 20
print(a.index(20, 3))
# 从索引位置 5 到 7 这个区间,第一次出现 30 元素的位置
print(a.index(30, 5, 7))


☆ count()
count() 可以返回指定元素在列表中出现的次数。如果列表中不存在指定元素,返回 0。
举个栗子:
# 2、count() 方法:统计元素在列表中出现的次数
list2 = ['刘备', '关羽', '张飞', '关羽', '赵云']
# 统计一下关羽这个元素在列表中出现的次数
print(list2.count('关羽'))
print(list2.count('小龙女'))

☆ index 和 count 函数效率如何?
效率很差,因为他们都要遍历元素。
index 如果找的元素是列表的第一个元素,此时的时间复杂度最小为 O(1),但是最坏的情况当要找的元素位于最后时,时间复杂度为 O(n)。
count 需要遍历整个列表来进行计数,时间复杂度为 O(n)。
遍历元素都跟当前列表的数据的个数有关,元素多称为规模大。
index 随着规模的增大而耗时增加,搜索效率随着规模增大而下降。
count 不管有多少元素,都需要完整遍历一遍,随着规模的增大而耗时增加。
性能极差,能不用则不用。在做算法题时,几乎不用。
使用索引访问列表元素是列表最好的使用方式,复杂度是 O(1)
☆ in && not in
判断列表中是否存在指定的元素,我们可以使用 count() 方法,返回 0 则表示不存在,返回大于 0 则表示存在。
但是,一般我们会使用更加简洁的 in 关键字来判断,直接返回 True 或 False。
in 效率高吗?
不高,因为需要遍历。和 index 函数一样,只是返回值不一样。
>>> # 本质是先比较地址是否一样,再比较内容
>>> [1] in [[1], [2]]
True
# 3、in 方法和 not in 方法(黑名单系统)
list3 = ['192.168.1.15', '10.1.1.100', '172.35.46.128']
if '10.1.1.100' in list3:
print('黑名单IP,禁止访问')
else:
print('正常IP,访问站点信息')

☆ len()
len() 返回列表长度,即列表中包含元素的个数。
#%%
a = [10, 20, 30]
len(a)

len 函数的时间复杂度为 O(1) 。
当创建一个列表时,列表内部会自动创建一个变量来记录列表长度。
☆ 增操作
当列表增加和删除元素时,列表会自动进行内存管理,大大减少了程序员的负担。但这个特点涉及列表元素的大量移动,效率较低。除非必要,我们一般只在列表的尾部添加元素 或删除元素,这会大大提高列表的操作效率。
| 函数 | 作用 |
|---|---|
append() |
增加指定数据到列表中 |
extend() |
列表结尾追加数据,如果数据是一个序列,则将这个序列的数据逐一添加到列表 |
insert() |
指定位置新增数据 |
☆ append()
append():在列表的尾部追加元素。原地修改列表对象,是真正的列表尾部添加新的元素,速度最快,推荐使用。
append 方法没有返回值 或者说 返回值为 None,没有返回值往往表示它自己被改变了,就地修改。
#%%
names = ['孙悟空', '唐僧', '猪八戒']
# 打印增加元素前的列表地址
print('增加元素前的列表地址', id(names))
# 在列表的尾部追加一个元素"沙僧"
names.append('沙僧')
# 打印增加元素后的列表地址
print('增加元素后的列表地址', id(names))
# 打印列表
print(names)

☆ extend() 方法
列表结尾追加数据,如果数据是一个序列,则将这个序列的数据逐一添加到列表。属于原地操作,不创建新的列表对象。
list1.extend(Iterator) 等价于:
for i in iterator:
list1.append()
案例:
#%%
list1 = ['Tom', 'Rose', 'Jack']
list2 = ['Hack', 'Jennify']
# 列表合并前的地址
print('列表合并后的地址', id(list1))
# 使用 extend 方法两个列表进行合并
list1.extend(list2)
# 列表合并后的地址
print('列表合并后的地址', id(list1))
print(list1)

l1 = [1, 2, 3]
# extend 的参数必须为一个可迭代对象
l1.extend(1)
print(l1)

☆ insert() 方法
使用 insert() 方法可以将指定的元素插入到列表对象的任意指定位置。这样会让插入位置后面所有的元素进行移动,会影响处理速度。涉及大量元素时,尽量避免使用。
类似发生这种移动的函数还有:remove()、pop()、del(),它们在删除非尾部元素时也会出现操作位置后面元素的移动。
insert (index, value)
作用:
在 index 的位置插入元素,如果右边界越界不报错,会将元素放到末尾;左边界小于 -(len - 1) 会将元素放到第一个位置。
names = ['薛宝钗', '林黛玉', '孙悟空', '猪八戒', '沙僧']
# 在薛宝钗和林黛玉之间,插入一个新元素"贾宝玉"
names.insert(1, '贾宝玉')
print(names)
names.insert(-2, '六耳猕猴')
names.insert(-20, '玉帝')
names.insert(20, '唐僧')
print(names)

☆ + 运算符操作
向列表末尾追加元素,并不是真正的尾部添加元素,而是创建新的列表对象;将原列表的元素和新列表的元素依次复制到新的列表对象中。这样,会涉及大量的复制操作,对于操作大量元素不建议使用。
本质上调用的是列表的魔术方法 __add()__ 方法。
#%%
l1 = [20, 30]
# 添加元素前
print('添加元素前', id(l1))
l1 = l1 + [50]
# 添加元素后
print('添加元素后', id(l1))

☆ 乘法扩展
使用乘法扩展列表,生成一个新列表,新列表元素是原列表元素的多次重复。
#%%
a = ['小龙女',100]
b = a * 3
print(a)
print(b)

适用于乘法操作的,还有:字符串、元组。例如:
#%%
c = '小龙女'
d = c * 3
print(c)
print(d)

☆ 删操作
| 函数 | 作用 |
|---|---|
del 列表[索引] |
删除列表中的某个元素 |
pop() |
删除指定下标的数据(默认为最后一个),并返回该数据 |
remove() |
移除列表中某个数据的第一个匹配项。如果不存在,抛异常 |
clear() |
清空列表,删除列表中的所有元素,返回空列表。 |
☆ del 删除指定的列表元素
基本语法:
names = ['Tom', 'Rose', 'Jack', 'Jennify']
# 删除Rose
del names[1]
# 打印列表
print(names)

删除列表指定位置的元素:
#%%
a = [100,200,888,300,400]
del a[2]
a


☆ pop() 方法
作用:删除指定下标的元素,如果不填写下标,默认删除最后一个。其返回结果:就是删除的这个元素。
#%%
names = ['貂蝉', '吕布', '董卓']
del_name = names.pop()
print(del_name)
print(names)
#%%
names = ['貂蝉', '吕布', '董卓']
del_name = names.pop(1)
print(del_name)
print(names)

l1 = [1, 2, 3]
# 报索引越界异常
print(l1.pop(9))

☆ remove() 方法
作用:删除首次出现的指定元素,若不存在该元素抛出异常。
#%%
fruit = ['apple', 'banana', 'pineapple', 'banana']
fruit.remove('banana')
print(fruit)
#%%
fruit = ['apple', 'banana', 'pineapple', 'banana']
# 列表中没有该元素,抛异常
fruit.remove('peach')
print(fruit)

☆ clear() 方法
清空列表,只是清空列表中的所有元素,列表本身并没有被删除。
#%%
names = ['貂蝉', '吕布', '董卓']
# 随着故事的发展,人物都 game over
names.clear()
# 打印列表
print(names)

☆ 改操作
| 函数 | 作用 |
|---|---|
列表[索引] = 修改后的值 |
修改列表中的某个元素 |
reverse() |
将数据序列进行倒叙排列 |
sort() |
对列表序列进行排序 |
copy() |
对列表序列进行拷贝 |
☆ 索引修改列表内容
list1 = ['貂蝉', '大乔', '小乔', '八戒']
# 修改列表中的元素
list1[3] = '周瑜'
print(list1)
list2 = [1, 6, 5, 9, 1, 3]
list2.reverse()
print("列表倒序排列:", list2)
list3 = [10, 50, 20, 30, 1]
list3.sort() # 升序(从小到大)
print("列表升序排列", list3)
# 或
list3.sort(reverse=True) # 降序(从大到小)
print("列表降序排列", list3)
list4 = list3.copy()
print("列表复制", list4)

通过索引修改列表效率非常高,时间复杂度为 O(1)。
☆sort()
修改原列表,不建新列表的排序
对列表序列进行排序,默认是升序排序。
sort(key, reverse=False)
-
key:排序函数,传递一个高阶函数。l1 = [1, 2, 'a', 7] l1.sort(key=str) # 类似于 if str(2) > str('a') print(l1)

reverse:False 升序排列,True 降序排列,默认为 False。
#%%
a = [20, 10, 30, 40]
# 默认是升序排列
a.sort()
print(a)
# 降序排列
a.sort(reverse=True)
print(a)
# 打乱顺序
import random
random.shuffle(a)
print(a)

☆ sorted()
建新列表的排序。
我们也可以通过内置函数 sorted() 进行排序,这个方法返回新列表,不对原列表做修改。
#%%
a = [20, 10, 30, 40]
print('原列表地址', id(a))
# 默认升序排列
b = sorted(a)
print('升序排列列表地址', id(b))
print(b)
# 降序排列
c = sorted(a, reverse=True)
print('降序排列列表地址', id(c))
print(c)

通过上面操作,我们可以看出,生成的列表对象 b 和 c 都是完全新的列表对象。
☆ reversed()
返回迭代器。
内置函数 reversed() 也支持进行逆序排列,与列表对象 reverse() 方法不同的是,内置函数 reversed() 不对原列表做任何修改,只是返回一个逆序排列的迭代器对象。
#%%
a = [20, 10, 30, 40]
b = reversed(a)
print(b)
print(list(b))

我们打印输出 c 发现提示是:list_reverseiterator。也就是一个迭代对象。
同时,我们使用 list(c) 进行输出,发现只能使用一次。第一次输出了元素,第二次为空。那是因为迭代对象在第一次时已经遍历结束了,第二次不能再使用。
☆ 列表相关的其他内置函数汇总
copy()
复制一个新的列表,是浅复制。
#%%
l1 = [1, 2, 'str', [1, 2]]
l2 = l1.copy()
print(id(l1))
print(id(l2))
#%%
print('修改前:')
print(l1)
print(l2)
l2[3][0] = 100
print('修改后:')
print(l1)
print(l2)

max 和 min
用于返回列表中最大和最小值。
#%%
a = [3, 10, 20, 15, 9]
print('最大值', max(a))
print('最小值', min(a))

sum
对数值型列表的所有元素进行求和操作,对非数值型列表运算则会报错。
#%%
a = [3, 10, 20, 15, 9]
sum(a)
#%%
b = ['xcz', 10, 'dd']
sum(b)

☆ 切片操作
我们在前面学习字符串时,学习过字符串的切片操作,对于列表的切片操作和字符串类似。切片是 Python 序列及其重要的操作,适用于列表、元组、字符串等等。切片 slice 操作可以让我们快速提取子列表或修改。切片的格式如下:
[起始偏移量 start: 终止偏移量 end [:步长 step]]
注:当步长省略时顺便可以省略第二个冒号。
- 典型操作(三个量为正数的情况)如下:
| 操作和说明 | 示例 | 结果 |
|---|---|---|
[:] 提取整个列表 |
[10, 20, 30][:] |
[10,20,30] |
[start:] 从 start 索引开始到结尾 |
[10, 20, 30][1:] |
[20,30] |
[:end] 从头开始直到 end-1 |
[10, 20, 30][:2] |
[10,20] |
[start: end] 从 start 到 end-1 |
[10, 20, 30, 40][1: 3] |
[20,30] |
[start: end :step] 从 start 提取到 end-1,步长是 step |
[10, 20, 30, 40, 50, 60, 70][1: 6: 2] |
[20, 40, 60] |
- 其他操作(三个量为负数)的情况:
| 说明 | 示例 | 结果 |
|---|---|---|
| 倒数三个 | [10, 20, 30, 40, 50, 60, 70][-3:] |
[50,60,70] |
| 倒数第五个到倒数第三个(包左不包右) | [10, 20, 30, 40, 50, 60, 70][-5: -3] |
[30,40] |
| 步长为负,从右到左反向提取 | [10, 20, 30, 40, 50, 60, 70][::-1] |
[70, 60, 50, 40, 30, 20, 10] |
切片操作时,起始偏移量和终止偏移量不在 [-(长度), 长度 - 1] 这个范围,也不会报错。起始偏移量小于 -(长度) 则会当做 0,终止偏移量大于 长度 - 1 会被当成 长度 - 1。
例如:
#%%
# 正常输出了结果,没有报错
[10, 20, 30, 40][1: 30]

☆ 通过索引直接访问元素
我们可以通过索引直接访问元素。索引的区间在 [-列表长度, 列表长度 - 1] 这个范围。超过这个范围则会抛出异常。
#%%
a = [10, 20, 30, 40, 50, 20, 30, 20, 30]
print(a[2])
print(a[10])

2.4 列表的循环遍历
while 循环:
list1 = ['貂蝉', '大乔', '小乔']
# 定义计数器
i = 0
# 编写循环条件
while i < len(list1):
print(list1[i])
# 更新计数器
i += 1

for 循环(个人比较推荐):
list1 = ['貂蝉', '大乔', '小乔']
for i in list1:
print(i)

2.5 列表的嵌套
列表的嵌套:列表中又有一个列表,我们把这种情况就称之为列表嵌套。
二维列表可以帮助我们存储二维、表格的数据。例如下表的数据:
| 姓名 | 年龄 | 薪资 | 城市 |
|---|---|---|---|
| 小龙女 | 18 | 30000 | 北京 |
| 黄蓉 | 19 | 20000 | 上海 |
| 赵敏 | 20 | 10000 | 深圳 |
#%%
a = [
["小龙女", 18, 30000, "北京"],
["黄蓉", 19, 20000, "上海"],
["赵敏", 20, 10000, "深圳"],
]
print(a[1][0], a[1][1], a[1][2])

嵌套循环打印二维列表所有的数据:
#%%
a = [
["小龙女", 18, 30000, "北京"],
["黄蓉", 19, 20000, "上海"],
["赵敏", 20, 10000, "深圳"],
]
for i in a:
for j in i:
print(j, end=" ")
print()

3 元组
3.1 为什么需要元组
num_list = [10, 20, 30]
num_list[0] = 100
那这种情况下,我们想要存储多个数据且数据不允许更改,应该怎么办呢?
答:使用元组,元组可以存储多个数据且元组内的数据是不能修改的。
列表属于可变序列,可以任意修改列表中的元素。元组属于不可变序列,不能修改元组中的元素。因此,元组没有增加元素、修改元素、删除元素相关的方法。
所以,我们只需要学习元组的创建和删除,元组中元素的访问和计数即可。元组支持如下操作:
- 索引访问
- 切片操作
- 连接操作
- 成员关系操作
- 比较运算操作
- 计数:元组长度
len()、最大值max()、最小值min()、求和sum()等。
3.2 元组的定义
元组特点:定义元组使用小括号,且使用逗号隔开各个数据,数据可以是不同的数据类型。
通过 () 创建元组
通过 () 创建元组,小括号可以省略。
#%%
# 创建一个空元组
a = ()
type(a)
#%%
a = (10, 20, 30)
a
#%%
# 可以省略括号
a = 10, 20, 30
a

如果元组只有一个元素,则必须后面加逗号。这是因为解释器会把 (1) 解释为整数 1,(1, ) 解释为元组。
#%%
a = (1)
print(type(a))
a = (1,)
print(type(a))
# 可省略括号
a = 1,
print(type(a))

通过 tuple() 创建元组
tuple(可迭代的对象)
#%%
# 创建一个空元组对象
b = tuple()
print(b)
b = tuple("abc")
print(b)
b = tuple(range(3))
print(b)
b = tuple([2,3,4])
print(b)

元组优化问题
#%%
a = tuple()
b = ()
a is b

#%%
a = tuple((1, ))
b = (1, )
a is b

3.3 元组的相关操作方法
由于元组中的数据不允许直接修改,所以其操作方法大部分为查询方法。
| 函数 | 作用 |
|---|---|
元组[索引] |
根据索引下标查找元素 |
index() |
查找某个数据,如果数据存在返回对应的下标,否则报错,语法和列表、字符串的 index 方法相同 |
count() |
统计某个数据在当前元组出现的次数 |
len() |
统计元组中数据的个数 |
案例 1:修改元组中的某个元素。元组不可以修改,如果修改元组的值会抛出异常。
#%%
a = (20, 10, 30, 9, 8)
a[2] = 33

案例 2:访问元组中的某个元素。
#%%
a = (20, 10, 30, 9, 8)
print(a[1])
print(a[1: 3])
print(a[:4])

案例 3:查找某个元素在元组中出现的位置,存在则返回索引下标,不存在则直接报错。
nums = (10, 20, 30)
print(nums.index(20))

案例 4:统计某个元素在元组中出现的次数。
nums = (10, 20, 30, 50, 30)
print(nums.count(30))

案例 5:len() 方法主要就是求数据序列的长度,字符串、列表、元组。
nums = (10, 20, 30, 50, 30)
print(len(nums))

列表关于排序的方法 list.sort() 是修改原列表对象,元组没有该方法。如果要对元组排序,只能使用内置函数 sorted(tupleObj),并生成新的列表对象。
#%%
a = (20, 10, 30, 9, 8)
b = sorted(a)
print(b)

3.4 zip
zip(列表 1,列表 2,…) 将多个列表对应位置的元素组合成为元组,并返回这个 zip 对象。
#%%
a = [10, 20, 30]
b = [40, 50, 60, 99]
c = [70, 80, 90]
d = zip(a, b, c)
print(d)
print(list(d))

压缩后的元组个数等于最短的列表的长度。
3.5 生成器推导式创建元组
从形式上看,生成器推导式与列表推导式类似,只是生成器推导式使用小括号。列表推导式直接生成列表对象,生成器推导式生成的不是列表也不是元组,而是一个生成器对象。
我们可以将生成器对象,转化成列表或者元组。也可以使用生成器对象的 __next__() 方法进行遍历,或者直接作为迭代器对象来使用。但不管什么方式使用,元素访问结束后,如果需要重新访问其中的元素,必须重新创建该生成器对象。
#%%
s = (i for i in range(5))
s
#%%
print(tuple(s))
#只能访问一次元素。第二次就为空了。需要再生成一次
print(list(s))
#%%
s = (x*2 for x in range(5))
print(s.__next__())
print(s.__next__())
print(s.__next__())
print(s.__next__())
print(s.__next__())
# 已经没有元素,再打印,抛出异常
print(s.__next__())


3.6 元组总结
- 元组的核心特点是:不可变序列。
- 元组的访问和处理速度比列表快。
- 与整数和字符串一样,元组可以作为字典的键,列表则永远不能作为字典的键使用。
4 字典
4.1 为什么需要字典
思考 1:比如我们要存储一个人的信息,姓名:Tom,年龄:20 周岁,性别:男,如何快速存储。
person = ['Tom', '20', '男']
思考 2:在日常生活中,姓名、年龄以及性别同属于一个人的基本特征。但是如果使用列表对其进行存储,则分散为 3 个元素,这显然不合逻辑。我们有没有办法,将其保存在同一个元素中,姓名、年龄以及性别都作为这个元素的 3 个属性。
答:使用 Python 中的字典。
4.2 Python 中字典 (dict) 的概念
字典是键值对的无序可变结构,字典中的每个元素都是一个键值对,包含:键对象和值对象。可以通过键对象实现快速获取、删除、更新对应的值对象。
列表中我们通过下标数字找到对应的对象。字典中通过键对象找到对应的值对象。
键是任意的不可变数据,比如:整数、浮点数、字符串、元组。
列表、字典、集合这些可变对象,不能作为键。并且键不可重复。
值可以是任意的数据,并且可重复。
特点:
- 符号为大括号(花括号) =>
{} - 数据为键值对形式出现 =>
{key:value},key:键名,value:值,在同一个字典中,key 必须是唯一(类似于索引下标) - 各个键值对之间用逗号隔开
定义:
# 有数据字典
dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
# 空字典
dict2 = {}
dict3 = dict()
4.3 字典的创建
-
我们可以通过
{}、dict()来创建字典对象。#%% a = {'name': 'Tom', 'age': 18, 'job': 'programmer'} # 使用 name=value 对初始化一个字典 b = dict(name='Tom', age=18, job='programmer') # 使用可迭代对象的二元组 c = dict([["name", "Tom"], ("age", 18)]) d = {} # 空的字典对象 e = dict() # 空的字典对象 print("a", a) print("b", b) print("c", c) print("d", d) print("e", e)![images/5.内建数据结构/Pasted-image-20250323193812.png]()
dict(**kwargs)使用key=value键值对对初始化一个字典。dict(iterable, **kwarg)使用可迭代对象和key=value对构造字典,不过可迭代对象的元素必须是一个二元结构。dict(mapping, **kwarg)使用一个字典构建另一个字典。#%% # 使用 name=value 对初始化一个字典 a = dict(name='Tom', age=18, job='programmer') # 使用可迭代对象和 name=value 对构造字典 b = dict([("name", "Tom"), ("age", 18)], job='programmer') # 使用一个字典构建另一个字典 c = dict(b, address='Beijing') print("a", a) print("b", b) print("c", c)![images/5.内建数据结构/Pasted-image-20250323194552.png]()
#%% d1 = {'name': '小龙女', 'age': 18} # 使用一个字典构建另一个字典 d2 = dict(d1, salary = 2000) print(d2)![images/5.内建数据结构/image-20220920212255819.png]()
#%% dict([('name', 'jack'), ('age', 18)])![images/5.内建数据结构/image-20220920210541270.png]()
#%% dict([('name', 'jack'), ('age', 18)], [('salary', 2000), ('address', 'BeiJing')])![images/5.内建数据结构/image-20220920210734713.png]()
#%% dict([('name', 'jack'), ('age', 18)], salary = 2000, address='BeiJing')![images/5.内建数据结构/image-20220920211009304.png]()
#%% dict(name = '小龙女', [('age', 17)])![images/5.内建数据结构/image-20220920211136524.png]()
-
通过
zip()创建字典对象。#%% k = ['name', 'age', 'job', 'a'] v = ['gaoqi', 18, 'techer'] d = dict(zip(k, v)) d![images/5.内建数据结构/Snipaste_2022-06-28_20-32-23.png]()
-
通过
fromkeys(iterable, value)创建值为空的字典。- iterable:可迭代对象,将可迭代对象中的每一个元素作为字典的键。
- value:缺省值,如果没有设置 value 缺省值,默认将值设为 None。
#%% k = ['name','age','job', 'a'] # 无缺省值 value d = dict.fromkeys(k) d![images/5.内建数据结构/Snipaste_2022-06-28_20-34-10.png]()
#%% k = ['name', 'age', 'job', 'a'] # 设置缺省值 value d = dict.fromkeys(k, '我是缺省值') d![images/5.内建数据结构/image-20220920213059712.png]()
4.4 字典的增操作
☆ 给字典新增“键值对”
基本语法:
字典名称[key] = value
案例:定义一个空字典,然后添加 name、age 以及 address 这样的 3 个 key。
#%%
# 1、定义一个空字典
person = {}
# 2、向字典中添加数据
person['name'] = '小龙女'
person['age'] = 18
person['address'] = '活死人墓'
# 3、使用print方法打印person字典
print(person)

4.5 字典的删操作
☆ del 字典名称 [key]
删除指定元素。
# 1、定义一个有数据的字典
person = {'name': '王大锤', 'age': 28, 'gender': 'male', 'address': '北京市海淀区'}
# 2、删除字典中的某个元素(如 gender)
del person['gender']
# 3、打印字典
print(person)

☆ clear() 方法
清空字典中的所有 key。
# 1、定义一个有数据的字典
person = {'name':'王大锤', 'age':28, 'gender':'male', 'address':'北京市海淀区'}
print(person)
# 2、使用 clear() 方法清空字典
person.clear()
# 3、打印字典
print(person)

☆ pop() 方法
pop() 删除指定键值对,并返回对应的“值对象”。
a = {'name': '小龙女', 'age': 18, 'job': 'programmer'}
b = a.pop('job')
print(a)
print(b)

☆ popitem() 方法
随机删除和返回该键值对。字典是无序可变序列,因此没有第一个元素、最后一个元素的概念;popitem 弹出随机的项,因为字典并没有最后的元素或者其他有关顺序的概念。若想一个接一个地移除并处理项,这个方法就非常有效(因为不用首先获取键的列表)。
#%%
a = {'name':'小龙女', 'age':18, 'job':'programmer'}
while len(a) > 0:
print(a.popitem())

4.6 字典的改操作
☆ 修改键值对
基本语法:
字典名称[key] = value
案例:定义一个字典,里面有 name、age 以及 address,修改 address 这个 key 的 value 值。
# 1、定义字典
person = {'name': '孙悟空', 'age': 600, 'address': '花果山'}
# 2、修改字典中的数据(address)
person['address'] = '东土大唐'
# 3、打印字典
print(person)

☆ update()
使用 update() 将新字典中所有键值对全部添加到旧字典对象上。如果 key 有重复,则直接覆盖。
#%%
a = {'name': '小龙女', 'age': 18, 'job': 'programmer'}
b = {'name': '赵敏', 'money': 1000, 'sex': '女'}
a.update(b)
a

4.7 字典的查操作
① 查询方法:使用具体的某个 key 查询数据,如果未找到,则直接报错。
字典序列[key]
#%%
a = {'name':'gaoqi','age':18,'job':'programmer'}
print(a['name'])
# 键不存在,抛出异常
print(a['sex'])

② 字典的相关查询方法
| 函数 | 作用 |
|---|---|
get(key[, default]) |
根据字典的 key 获取对应的 value 值,如果当前查找的 key 不存在则返回第二个参数(默认值),如果省略第二个参数,则返回 None |
setdefault(key[, default]) |
返回 key 对应 的值 value key 不存在,添加 kw 对,value 设置为 default,并返回 default,如果 default 没有设置,缺省为 None |
keys() |
以列表返回一个字典所有的键 |
values() |
以列表返回字典中的所有值 |
items() |
以列表返回可遍历的 (键, 值) 元组 |
☆ get()
通过 get() 方法获得值。推荐使用。
优点是:指定键不存在,返回 None;也可以设定指定键不存在时默认返回的对象。推荐使用 get() 获取值对象。
案例 1:使用 get 获取字典中某个 key 的 value 值。
# 1、定义一个字典
cat = {'name': 'Tom', 'age': 5, 'address': '美国纽约'}
# 2、获取字典的相关信息
name = cat.get('name')
age = cat.get('age')
gender = cat.get('gender', 'male') # get(key, 默认值)
address = cat.get('address')
print(f'姓名:{name},年龄:{age},性别:{gender},住址:{address}')

☆ setdefault(key, value)
setdefault(k, value) 方法用于设置 key 的默认值。
该方法接收两个参数,第 1 个参数表示 key,第 2 个参数 value 表示默认值。
如果 key 在字典中不存在,那么 setdefault() 方法会向字典中添加这个 key,并用第 2 个参数作为 key 的值。该方法会返回这个默认值。
如果未指定第 2 个参数,那么 key 的默认值是 None。如果字典中已经存在这个 key,setdefault() 不会修改 key 原来的值,而且该方法会返回 key 原来的值。
#%%
# 定义一个空字典
dict = {}
# 向字典中添加一个名为name的key,默认值是小龙女,输出结果:小龙女
print(dict.setdefault('name', '小龙女'))
# 输出结果:{'name': '小龙女'}
print(dict)
# 并没有改变name的值,输出结果:小龙女
print(dict.setdefault('name', '黄蓉'))
# 输出结果:{'name': '小龙女'}
print(dict)
# 向字典中添加一个名为age的key,默认值是None,输出结果:None
print(dict.setdefault('age'))
# 输出结果:{'name': '小龙女', 'age': None}
print(dict)

☆ keys()
返回字典中的所有的键对象。
案例 2:提取 person 字典中的所有 key。
# 1、定义一个字典
person = {'name': '貂蝉', 'age': 18, 'mobile': '13765022249'}
# 2、提取字典中的name、age以及mobile属性
print(type(person.keys()))
print(person.keys())

☆ values()
返回字典中的所有值对象。
案例 3:提取 person 字典中的所有 value 值。
# 1、定义一个字典
person = {'name': '貂蝉', 'age': 18, 'mobile': '13765022249'}
# 2、提取字典中的貂蝉、18以及13765022249号码
print(type(person.values()))
print(person.values())

☆ items()
返回可遍历的 (键, 值) 元组列表。
案例 4:使用 items() 方法提取数据。
# 1、定义一个字典
person = {'name': '貂蝉', 'age': 18, 'mobile': '13765022249'}
# 2、调用items方法获取数据,dict_items([('name', '貂蝉'), ('age', 18), ('mobile', '13765022249')])
print(type(person.items()))
print(person.items())
# 3、结合for循环对字典中的数据进行遍历
for key, value in person.items():
print(f'{key}:{value}')

注:
Python3 中,keys、values、items 方法返回一个类似一个生成器的可迭代对象,但是可以重复使用。
- Dictionary view 对象,可以使用
len()、iter()、in操作 - 字典的 entry 的动态的视图,字典变化,视图将反映出这些变化
- keys 返回一个类 set 对象,也就是可以看做一个 set 集合。如果 values 都可以 hash,那么 items 也可以看做是类 set 对象
- value 可以是任意合法值,不能保证唯一,也不一定能 hash,
values()方法返回可迭代对象,不是类 set 对象。
Python2 中,上面的方法会返回一个新的列表,立即占据新的内存空间。所以 Python 建议使用 iterkeys、itervalues、iteritems 版本,返回一个迭代器,而不是返回一个 copy。
#%%
d1 = dict([("name", "小龙女"), ("age", 18), ("salary", 2000)], address="活死人墓")
# %%
print(type(d1.keys()))
print(type(d1.values()))
print(type(d1.items()))
# %%
# 创建字典视图
keys = d1.keys()
values = d1.values()
items = d1.items()
print("字典更新前keys:", keys)
print("字典更新前values:", values)
print("字典更新前items:", items)
print("==========================")
d1.update({"门派": "古墓派"}, cp="杨过", age=19)
# 字典的视图不会立即返回结果,可以展示字典发生的变化
print("字典更新后keys:", keys)
print("字典更新后values:", values)
print("字典更新后items:", items)


☆ len()
返回字典中键值对的个数。
#%%
person = {'name': '貂蝉', 'age': 18, 'mobile': '13765022249'}
len(person)

☆ in
默认检测一个键是否在字典中。
#%%
person = {'name': '貂蝉', 'age': 18, 'mobile': '13765022249'}
# 默认检测键是否在字典中
print('name' in person)
# 检测值是否在字典中
print('貂蝉' in person.values())

4.8 字典的“有序性”
字典元素是按照 key 的 hash 值无序存储的。
但是,有时候我们却需要一个有序的元素顺序,Python3.6 之前,使用 OrderedDict 类可以做到,3.6 开始 dict 自身支持。到底 Python 对一个无序数据结构记录了什么顺序?
注意:有序指的是记住了输入序,并不是排序。


Python3.6 之前,在不同的机器上,甚至同一个程序分别运行 2 次,都不能确定不同的 key 的先后顺序。


Python3.6+,记录了字典 key 的录入顺序,遍历的时候,就是按照这个顺序。
如果使用 d = {'a': 300, 'b': 20, 'c': 10, 'd': 50},就会造成以为字典按照 key 排序的错觉。
目前,建议不要 3.6 提供的这种字典特性,还是认为字典返回的是无序的。如果非要这种有序,建议使用 OrderedDict。
4.9 序列解包
序列解包可以用于元组、列表、字典。序列解包可以让我们方便的对多个变量赋值。
x, y, z = (20, 30, 10)
print(f"x:{x}")
print(f"y:{y}")
print(f"z:{z}")
print("-" * 20)
(a, b, c) = (9, 8, 10)
print(f"a:{a}")
print(f"b:{b}")
print(f"c:{c}")
print("-" * 20)
[d, e, f] = [10, 20, 30]
print(f"d:{d}")
print(f"e:{e}")
print(f"f:{f}")
print("-" * 20)

序列解包用于字典时,默认是对键进行操作;如果需要对键值对操作,则需要使用 items();如果需要对值进行操作,则需要使用 values()。
s = {'name': 'gaoqi', 'age': 18, 'job': 'teacher'}
# 默认对键进行操作
name, age, job = s
print(f"name:{name}")
print(f"age:{age}")
print(f"job:{job}")
print("-" * 20)
# 对键值对进行操作
name, age, job = s.items()
print(f"name:{name}")
print(f"age:{age}")
print(f"job:{job}")
print("-" * 20)
# 对值进行操作
name, age, job = s.values()
print(f"name:{name}")
print(f"age:{age}")
print(f"job:{job}")
print("-" * 20)

4.10 封装和解构
基本概念
t1 = 1, 2
print(type(t1)) # 什么类型
t2 = (1, 2)
print(type(t2))

Python 等式右侧出现逗号分隔的多值的时候,就会将这几个值封装到元组中。这种操作称为封装 (packing)。
x, y = (1, 2)
print(f"x:{x}") # 1
print(f"y:{y}") # 2

Python 中等式右侧是一个容器类型,左侧是逗号分隔的多个标识符,则将右侧容器中数据和左侧标识符一一对应。这种操作称为解构(unpacking)。
从 Python3 开始,对解构做了很大的改进,现在用起来已经非常的方便快捷。
封装和解构是非常方便的提取数据的方法,在 Python、JavaScripts 等语言中应用极广。
# 交换数据
x = 4
y = 5
temp = x
x = y
y = temp
# 封装和解构,交换
x = 10
y = 11
x, y = y, x
简单解构
# 左右个数相同
a, b = 1, 2
a, b = (1, 2)
a, b = [1, 2]
a, b = [10, 20]
a, b = {10, 20} # 非线性结构
a, b = {'a': 10, 'b': 20} # 非线性结构也可以解构
[a, b] = (1, 2)
[a, b] = 10, 20
(a, b) = {30, 40}
等号左边只能是线性数据结构,等号右边可以是线性数据结构或非线性数据结构。左边的小括号或中括号可以省略不写,在嵌套使解构时用。
那么,左右个数不一致可以吗?
a, b = (10, 20, 30)

例:如何将下列所有数子都解构出来?
(1, [2, 3, 4], 5)
下面这种写法显然不对,左右个数不一致。
a, b, c, d, e = (1, [2, 3, 4], 5)
print(a, b, c, d, e)

正确写法:
a, [b, c, d], e = (1, [2, 3, 4], 5)
print(a, b, c, d, e)

剩余变量解构
在 Python3.0 中增加了剩余变量解构(rest)。
a, *rest, b = [1, 2, 3, 4, 5]
print(a, b)
print(type(rest), rest) # <class 'list'>[2,3,4]

标识符 rest 将尽可能收集剩余的数据组成一个列表。
a, *rest = [1, 2, 3, 4, 5]
print(a, rest)
a, *rest, b = [1, 2, 3, 4, 5]
print(a, rest, b) # 内容是什么?
*rest, b = [1, 2, 3, 4, 5]
print(rest, b) # 内容是什么?

*rest = [1, 2, 3, 4, 5]
print(rest) # 内容是什么?

a, *r1, *r2, b = [1, 2, 3, 4, 5] # ?

a, *_, b = [1, 2, 3, 4, 5]
print(_)

_, *b, _ = [1, 2, 3]
print(_) # 第一个是什么
print(b) # 是什么
print(_) # 第二个是什么

在 IPython 中实验,_ 是最后一个输出值,这里将把它覆盖。
_ 是合法的标识符,这里它没有什么可读性,它在这里的作用就是表示不关心这个变量的值,我不想要。有人把它称作丢弃变量。
4.11 字典核心底层原理 (重要)
字典对象的核心是散列表。散列表是一个稀疏数组(总是有空白元素的数组),数组的每个单元叫做 bucket。
每个 bucket 有两部分:一个是键对象的引用,一个是值对象的引用。
由于所有 bucket 结构和大小一致,我们可以通过偏移量来读取指定 bucket。

将一个键值对放进字典的底层过程
a = {}
a["name"]="小龙女"
假设字典 a 对象创建完后,数组长度为 8:

我们要把 name=”gaoqi” 这个键值对放到字典对象 a 中,首先第一步需要计算键 name 的散列值。Python 中可以通过 hash() 来计算。
>>> bin(hash("name"))
'-0b1010111101001110110101100100101'
由于数组长度为 8,我们可以拿计算出的散列值的最右边 3 位数字作为偏移量,即“101”,十进制是数字 5。我们查看偏移量 5,对应的 bucket 是否为空。如果为空,则将键值对放进去。如果不为空,则依次取右边 3 位作为偏移量,即“100”,十进制是数字 4。再查看偏移量为 4 的 bucket 是否为空。直到找到为空的 bucket 将键值对放进去。流程图如下:


扩容
Python 会根据散列表的拥挤程度扩容。
扩容指的是:创造更大的数组,将原有内容拷贝到新数组中。接近 2/3 时,数组就会扩容。
根据键查找“键值对”的底层过程
我们明白了,一个键值对是如何存储到数组中的,根据键对象取到值对象,理解起来就简单了。
>>> a.get("name")
'gaoqi'
当我们调用 a.get("name"),就是根据键 name 查找到“键值对”,从而找到值对象 gaoqi。
第一步,我们仍然要计算 name 对象的散列值:
>>> bin(hash("name"))
'-0b1010111101001110110101100100101'
和存储的底层流程算法一致,也是依次取散列值的不同位置的数字。假设数组长度为 8,我们可以拿计算出的散列值的最右边 3 位数字作为偏移量,即“101”,十进制是数字 5。我们查看偏移量 5,对应的 bucket 是否为空。如果为空,则返回 None。如果不为空,则将这个 bucket 的键对象计算对应散列值,和我们的散列值进行比较,如果相等。则将对应“值对象”返回。如果不相等,则再依次取其他几位数字,重新计算偏移量。依次取完后,仍然没有找到。则返回 None。流程图如下:

用法总结
-
键必须可散列。
- 数字、字符串、元组,都是可散列的。
- 自定义对象需要支持下面三点:
- 支持
hash()函数。 - 支持通过
__eq__ ()方法检测相等性。 - 若
a == b为真,则hash(a) == hash(b)也为真。
- 支持
-
字典在内存中开销巨大,典型的空间换时间。
-
键查询速度很快。
-
往字典里面添加新键可能导致扩容,导致散列表中键的次序变化。因此,不要在遍历字典的同时进行字典的修改。
4.12 综合案例:通讯录管理系统
需求:开个一个通讯录的管理系统,主要用于实现存储班级中同学的信息(姓名、年龄、电话)。
知识点:列表、字典、死循环
延伸:在 Python 中,我们可以使用字典来保存一个人的基本信息。但是如果想保存多个人的信息,我们必须把列表和字典结合起来。
students = [0, 1, 2]
student = {'name': '刘备', 'age': 18, 'mobile': '10086'}
# 组装:
students = [
{'name': '刘备', 'age': 18, 'mobile': '10086'},
{'name': '关羽', 'age': 17, 'mobile': '10000'},
{'name': '张飞', 'age': 16, 'mobile': '10010'}
]
为什么需要死循环:
当我们选中某个菜单时,功能一旦执行完毕,则整个程序就执行结束了。为了保存程序可以一直运行下去,可以模拟死循环的效果,让程序一直运行下去。
while True:
...
要求:正常情况下,通讯录管理系统应该有 4 个功能:增删改查。
# 1、定义一个列表,将来用于存储所有学员的通讯信息
students = []
# 2、打印功能菜单
print('-' * 40)
print('欢迎使用传智教育通讯录管理系统V1.0')
print('[1] 增加学员信息')
print('[2] 删除学员信息')
print('[3] 退出系统')
print('-' * 40)
while True:
# 3、提示用户进行相关操作
user_num = int(input('请输入您要进行的操作编号:'))
if user_num == 1:
# 4、提示用户输入学员的信息
student = {}
student['name'] = input('请输入学员的姓名:')
student['age'] = int(input('请输入学员的年龄:'))
student['mobile'] = input('请输入学员的电话:')
# 5、把学员信息保存在列表中
students.append(student)
print(students)
elif user_num == 2:
name = input('请输入要删除的学员信息:')
# 6、遍历所有学员信息
for i in students:
if i['name'] == name:
# 从列表中删除整个学员(字典)
students.remove(i)
print('删除成功')
print(students)
else:
print('您要删除的学员信息不存在')
elif user_num == 3:
print('感谢您使用传智教育通讯录管理系统V1.0')
break
else:
print('输入错误,请重新输入要操作的编号')
5 集合
5.1 什么是集合
集合是无序可变序列,元素不能重复。
实际上,集合底层是字典实现,集合的所有元素都是字典中的键对象,因此是不能重复且唯一的。
集合(set)是一个无序的不重复元素序列。
- 天生去重
- 无序
5.2 集合的定义
在 Python 中,我们可以使用一对花括号 {} 或者 set() 方法来定义集合,但是如果你定义的集合是一个空集合,则只能使用 set() 方法。
使用 set(),将列表、元组等可迭代对象转成集合。如果原来数据存在重复数据,则只保留一个。
# 定义一个集合
s1 = {10, 20, 30, 40, 50}
print(s1)
print(type(s1))
# 定义一个集合:集合中存在相同的数据
s2 = {'刘备', '曹操', '孙权', '曹操'}
print(s2)
print(type(s2))
print('################')
# 使用 set() 方法将可迭代对象转换成集合
# 如果原来数据存在重复数据,则只保留一个。
l1 = [1, 2, 3, 3, 5, 6, 6]
tu = 1, 2, 3, 3, 5, 6, 6
s3 = set(l1)
s4 = set(tu)
print(s3)
print(s4)
print('################')
# 定义空集合
s5 = {}
s6 = set()
print(type(s5)) # <class 'dict'>
print(type(s6)) # <class 'set'>

5.3 集合操作的相关方法(增删查)
☆ 集合的增操作
-
add()方法:向集合中增加一个元素(单一)。students = set() students.add('小龙女') students.add('赵敏') print(students)![images/5.内建数据结构/Pasted-image-20250926173917.png]()
-
update()方法:向集合中增加可迭代类型的数据(字符串、列表、元组、字典)。students = set() list1 = ['刘备', '关羽', '赵云'] students.update(list1) print(students)![images/5.内建数据结构/Pasted-image-20250926174011.png]()
students = set()
students.add('刘德华')
students.add('黎明')
# 使用 update 新增元素
students.update('蔡徐坤')
print(students)

☆ 集合的删操作
remove()方法:删除集合中的指定数据,如果数据不存在则报错。discard()方法:删除集合中的指定数据,如果数据不存在也不会报错。pop()方法:随机删除集合中的某个数据,并返回这个数据。
# 1、定义一个集合
products = {'萝卜', '白菜', '水蜜桃', '奥利奥', '西红柿', '凤梨'}
# 2、使用 remove 方法删除白菜这个元素
products.remove('白菜')
print(products)
# 3、使用 discard 方法删除未知元素
products.discard('玉米')
print(products)
# 4、使用 pop 方法随机删除某个元素
del_product = products.pop()
print(del_product)
# 2、使用 remove 方法删除不存在元素
products.remove('小龙女')

☆ 集合中的查操作
in:判断某个元素是否在集合中,如果在,则返回 True,否则返回 False。not in:判断某个元素不在集合中,如果不在,则返回 True,否则返回 False。
# 定义一个set集合
s1 = {'刘帅', '英标', '高源'}
# 判断刘帅是否在s1集合中
if '刘帅' in s1:
print('刘帅在s1集合中')
else:
print('刘帅没有出现在s1集合中')

- 集合的遍历操作
for i in 集合:
print(i)
5.4 集合中的交集、并集与差集特性
在 Python 中,我们可以使用 & 来求两个集合的交集:

在 Python 中,我们可以使用 | 来求两个集合的并集:

在 Python 中,我们可以使用 - 来求两个集合的差集:

# 求集合中的交集、并集、差集
s1 = {'刘备', '关羽', '张飞', '貂蝉'}
s2 = {'袁绍', '吕布', '曹操', '貂蝉'}
# 求两个集合中的交集
print(s1 & s2)
# 求两个集合中的并集
print(s1 | s2)
# 求连个集合中的差集
print(s1 - s2)
print(s2 - s1)

6 序列中的公共方法
6.1 什么是公共方法
所谓的公共方法就是支持操作大部分数据序列的方法。
6.2 常见公共方法 1
| 运算符 | 描述 | 支持的容器类型 |
|---|---|---|
+ |
合并 | 字符串、列表、元组 |
* |
复制 | 字符串、列表、元组 |
in |
元素是否存在 | 字符串、列表、元组、字典、集合 |
not in |
元素是否不存在 | 字符串、列表、元组、字典、集合 |
案例 1:+ 合并。
# 1、+ 加号,代表两个序列之间的连接与整合
str1 = 'hello'
str2 = 'world'
print(str1 + str2)
# 2、定义两个列表,对其数据进行整合
list1 = ['刘备', '关羽']
list2 = ['诸葛亮', '赵云']
print(list1 + list2)
# 3、定义两个元组,对其数据进行整合
tuple1 = (10, 20)
tuple2 = (30, 40)
print(tuple1 + tuple2)

案例 2:* 复制。
# 1、字符串与乘号的关系
print('-' * 40)
print('传智教育Python管理系统V1.0')
print('-' * 40)
# 2、列表与乘号的关系
list1 = ['*']
print(list1 * 10)
# 3、元组与乘号的关系
tuple1 = (10,)
print(tuple1 * 10)

案例 3:in 与 not in 方法。
ips = ['192.168.10.11', '10.1.1.100', '172.15.184.31']
if '10.1.1.100' in ips:
print('列表中元素已存在')
else:
print('列表中元素不存在')

6.3 常见公共方法 2
| 函数 | 描述 |
|---|---|
len() |
计算容器中元素个数 |
del 或 del() |
根据索引下标删除指定元素 |
max() |
返回容器中元素最大值 |
min() |
返回容器中元素最小值 |
range(start, end, step) |
生成从 start 到 end(不包含)的数字,步长为 step,供 for 循环使用 |
enumerate() |
函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。 |
案例 1:len() 获取字符串、列表、元组、字典、集合的长度。
# 定义一个字符串
str1 = 'hello world'
print(f'字符串的长度为{len(str1)}')
# 定义一个列表
list1 = [10, 20, 30, 40, 50]
print(f'列表的长度为{len(list1)}')
# 定义一个字典
dict1 = {'name': '哆啦A梦', 'gender': 'male', 'address': '东京'}
print(f'字典的长度为{len(dict1)}')

案例 2:del 方法,用于删除序列中指定的元素(根据索引下标)。
# 定义一个列表
list1 = ['吕布', '董卓', '貂蝉']
# 使用 del 方法删除董卓
del list1[1]
print(list1)
# 定义一个字典
dict1 = {'name': '白龙马', 'age': 23, 'address': '东海龙宫'}
# 使用 del 方法删除 age
del dict1['age']
print(dict1)
del (dict1['name'])
print(dict1)

案例 3:求某个序列中元素的最大值和最小值。
num1 = int(input('请输入第一个数:'))
num2 = int(input('请输入第二个数:'))
num3 = int(input('请输入第三个数:'))
list1 = [num1, num2, num3]
max_num = max(list1)
min_num = min(list1)
print(f'最大值:{max_num}')
print(f'最小值:{min_num}')
num1 = int(input('请输入第一个数:'))
num2 = int(input('请输入第二个数:'))
num3 = int(input('请输入第三个数:'))
list1 = [num1, num2, num3]
max_num = max(list1)
min_num = min(list1)
print(f'最大值:{max_num}')
print(f'最小值:{min_num}')

案例 4:enumerate(),把一个序列类型中的每个元素构造成 (index, value) 结构,然后结合 for 循环进行遍历。
list1 = [10, 20, 30, 40, 50]
n = 1
for i in list1:
print(f'第{n}个数:{i}')
n += 1
print('-' * 40)
for key, value in enumerate(list1):
print(f'第{key + 1}个数:{value}')

6.4 序列类型之间的相互转换
list() 方法:把某个序列类型的数据转化为列表。
# 1、定义元组类型的序列
tuple1 = (10, 20, 30)
print(list(tuple1))
# 2、定义一个集合类型的序列
set1 = {'a', 'b', 'c', 'd'}
print(list(set1))
# 3、定义一个字典
dict1 = {'name': '刘备', 'age': 18, 'address': '蜀中'}
print(list(dict1))

tuple() 方法:把某个序列类型的数据转化为元组。
# 定义一个列表类型的数据
list1 = ['a', 'b', 'c', 'd']
print(tuple(list1))
# 定义一个集合类型的数据
set1 = {10, 20, 30, 40}
print(tuple(set1))

set() 方法:将某个序列转换成集合(但是要注意两件事 => ① 集合可以快速完成列表去重 ② 集合不支持下标)。
# 定义一个列表类型的数据
list1 = ['a', 'b', 'c', 'd', 'a']
print(set(list1))
# 定义一个元组类型的数据
tuple1 = (10, 20, 30, 40)
print(set(tuple1))

7 元组的不可变与字典键对象的不可变
元组的不可变和字典键对象的不可变一样吗?
tu = ('a', 1, [1, 2, 3])
print(tu)

dict = {'a': 1, 1: 2, [1, 2, 3]: 3}

dict = {'a': 1, 1: 2, ([1, 2, 3]): 3}

同样是要求不可变,但是元组可以使用列表,而字典却不可以。
这是因为元组的不可变是指的索引(即变量在内存的地址)不可变,而字典的键对象要求的不可变是 hash 值 的不可变。
详细讲解请看 hash 详解:[[hash详解]]
8 各个数据结构比较
线性数据结构(有序,可索引):list、tuple、str
非线性数据结构(无序,不可索引):dict、set
如果内存中有线程列表,你需要做的操作,里面用到了遍历,时间复杂度为 O(n),n 规模越大,效率越低下。微秒 => 毫秒。如 index、count 方法。
如果内存中有集合,不管数据规模多大,它的检索元素的时间,不随着规模变化,时间复杂度为 O(1),效率极高。ns(纳秒)级别。如 key,in。













浙公网安备 33010602011771号