# Python教程 - 5 容器

什么是容器?

容器就是可以存储多个数据的一种数据类型,容器内的每一个数据称之为元素,其中的元素可以是任意类型的数据,包括字符串、数字、布尔,甚至是容器等等。

例如有一个班级,有40个学生,我们肯定不能定义40个变量来存储学生的信息,有1000个人,那不要命了,我们可以将所有学生的信息放到容器中。

容器分为5类:

  • 列表(list)
  • 字符串(str)
  • 元组(tuple)
  • 集合(set)
  • 字典(dict)

不同的容器有不同的特点,例如:

  • 是否支持重复的元素,有的容器中的元素不能重复,有的可以。
  • 是否可以修改
  • 是否有序

下面一一介绍。

# 5.1 列表(list)

列表就是一个普通的容器,满足你最原始的想象,主要有如下特点:

  • 列表中的元素可以重复
  • 列表中的元素可以修改,可以增加、修改、删除
  • 列表中的元素是有序的,可以通过索引来访问
  • 列表中可以存储不同数据类型的数据
  • 列表中最多可以容纳 2^63 - 1个元素,这辈子够你用了。

# 5.1.1 列表的定义

列表使用 方括号[] 定义,列表内每个元素使用 逗号, 分隔。

# 定义一个列表,并赋值给一个变量
变量名称 = [元素1, 元素2, 元素3, ...]

# 定义空列表
变量名称 = []				# 方式1
变量名称 = list()		# 方式2
1
2
3
4
5
6

举个栗子:

定义一个列表,其中存储 4 个人的名字:

item_list = ["zhangsan", 18, 3.14, True]			# 定义一个容器

print(item_list)				# 打印容器
print(type(item_list))	# 打印容器类型
1
2
3
4

执行结果:

['zhangsan', 18, 3.14, True]
<class 'list'>
1
2

可以看到列表可以存储多个数据,并且可以存储不同类型的数据。

打印列表会显示列表中所有的元素,打印列表类型可以看到列表的类型为list。

# 5.1.2 列表的索引

我们已经将数据存储到列表中了,那么如何获取列表中的元素呢?

可以通过元素的索引(也称之为下标)来取出指定位置的元素。

如上图,列表中的每个元素,都有一个索引,从0开始,依次递增。

只需要按照下标索引,就可以获取对应的元素。

举个栗子:

name_list = ["zhangsan", "lisi", "wangwu", "zhaoliu", "qianqi", "chenba"]			# 定义一个容器
print(name_list[0])
print(name_list[3])
1
2
3

执行结果:

zhangsan
zhaoliu
1
2

注意,在访问列表元素的时候,下标不能越界,例如上面的列表,索引最大是5,超过5就会出现下标越界的错误。

列表索引的访问,除了可以从0开始递增访问,还可以反向访问,如下图:

列表可以从后向前访问,从-1开始,依次递减。

举个栗子:

name_list = ["zhangsan", "lisi", "wangwu", "zhaoliu", "qianqi", "chenba"]			# 定义一个容器
print(name_list[-1])
print(name_list[-3])
1
2
3

执行结果:

chenba
zhaoliu
1
2

# 5.1.3 容器的嵌套

我们一开始说了,容器中可以存储任意类型的数据,包括容器类型。

所以容器是可以嵌套使用的,包括后面的元组(tuple)、集合(set)、字典(dict),可是相互嵌套使用。

举个栗子:

item_list = ["zhangsan", 18, 3.14, True, [1, 2, 3]]	
1

上面的列表中还包含了一个列表,也可以和后面的容器相互嵌套。

访问嵌套的列表,可以继续使用它下标来访问:

item_list = ["zhangsan", 18, 3.14, True, [1, 2, 3]]
print(item_list[-1][1])
1
2

item_list[-1] 得到的是[1, 2, 3] ,继续使用 item_list[-1][1],获取到的是其中的元素 2 。

# 5.1.4 列表常用操作

常用的操作有很多,例如列表中元素的插入、删除、清空、修改等。

# 1 列表长度

可以使用 len() 函数求列表或其他容器,包括字符串的长度。

这个是Python的内置函数。

举个栗子:

name_list = ["zhangsan", "lisi", "wangwu", "zhaoliu", "qianqi", "chenba"]			# 定义一个容器
print(len(name_list))

str = "埋骨何须桑梓地,人生何处不青山"
print(len(str))
1
2
3
4
5

执行结果:

6
15
1
2

所以获取列表的最后一个元素,可以使用 list[-1] 或 list[len(list) - 1] 。

# 2 查找元素

可以使用 列表.index() 方法查找元素在列表中的下标,如果找不到,会报错。

方法是通过对象来调用的 对象.方法(参数),和函数略有不同。

举个栗子:

int_list = [1, 2, 3, 1]
print(int_list.index(1))					# 查找1的位置
print(int_list.index(1, 1))				# 查找的时候,可以执行起始位置
print(int_list.index(1, 1, 6))		# 查找的时候,可以指定起始、结束位置
print(int_list(4))								# 找不到程序会报错
1
2
3
4
5

执行结果:

0
3
3
Traceback (most recent call last):
File "***/hello_world.py", line 5, in <module>;
 print(int_list(4))
       ^^^^^^^^^^^
TypeError: 'list' object is not callable
1
2
3
4
5
6
7
8

注意,只会返回找到的第一个位置。

查找的时候,可以指定查找的起始位置和结束位置,起始位置和结束位置的index可以超过数组的长度,没有越界问题。

找不到程序就会报错,程序就不能继续执行了,这不是坑爹吗?

不急,后面会学习异常捕获,再学习如何处理。

# 3 修改元素

修改元素,直接通过下标获取元素,直接重新赋值就可以了。

举个栗子:

int_list = [1, 2, 3, 1]
int_list[-1] = 4
print(int_list)
1
2
3

执行结果:

[1, 2, 3, 4]
1

注意,访问列表的时候,不要让下标越界。

# 4 插入元素

使用 列表.insert(下标, 元素) 方法,可以在指定下标位置,插入元素。

举个栗子:

int_list = [1, 2, 3, 1]
int_list.insert(3, 4)			# 在下标3的位置插入元素4
int_list.insert(8, 5)			# 在下标8的位置插入元素5,不会存在越界问题
print(int_list)
1
2
3
4

执行结果:

[1, 2, 3, 4, 1, 5]
1

插入时,指定的下标,不会存在越界问题。

# 5 追加元素

使用 列表.append(元素) 方法,可以在列表的末尾追加元素。

举个栗子:

int_list = [1, 2, 3, 1]
int_list.append(5)							# 追加元素
print(int_list)
1
2
3

执行结果:

[1, 2, 3, 1, 5]
1

还可以使用 列表.extend(容器) 方法,将其他容器(列表、元组、集合)中的元素,依次追加到列表末尾。

int_list = [1, 2, 3, 1]
int_list.extend([5, 6, 7])				# 追加容器中所有元素
print(int_list)
1
2
3

执行结果:

[1, 2, 3, 1, 5, 6, 7]
1

# 6 删除元素

删除元素有两种方式:一种是根据索引删除,一种是根据内容删除。

# 根据索引删除

根据索引删除又有两种方法:

方法一:del 列表[下标]

方法二:列表.pop(下标)

举个栗子:

int_list = [1, 2, 3, 1]
del int_list[-1]        # 删除最右一个元素
int_list.pop(1)         # 删除第二个元素
print(int_list)
1
2
3
4

执行结果:

[1, 3]
1

注意,删除的时候,列表下标不要越界,否则报错。

# 根据内容删除

根据内容删除,使用 .remove() 方法,该方法会从前向后检索,删除列表中第一个匹配的元素。

int_list = [1, 2, 3, 1]
int_list.remove(1)
print(int_list)
1
2
3

执行结果:

[2, 3, 1]
1

# 7 清空列表

可以使用 列表.clear() 方法,清空列表中的所有元素。

举个栗子:

int_list = [1, 2, 3, 1]
int_list.clear()					# 清空列表中所有的元素
print(int_list)
1
2
3

执行结果:

[]
1

# 8 统计指定元素的数量

可以使用 列表.count(元素) 方法 ,来统计列表内,指定元素的数量。

举个栗子:

int_list = [1, 2, 3, 1]
print(int_list.count(1))				# 统计列表中值为1的元素的数量
1
2

执行结果:

2
1

# 9 列表的遍历

容器中存储的是多个元素,所以经常需要遍历容器,依次取出容器里的元素,对其进行处理。

而对于列表,遍历的方式主要有两种:

  • for循环遍历
  • while循环变量
# for循环遍历

语法:

for 元素 in 容器:
    # 对元素进行处理
1
2

举个栗子:

int_list = [1, 2, 3, 4]
for item in int_list:				# 临时变量名称自定义
    print(item)
1
2
3

执行结果:

1
2
3
4
1
2
3
4
# while 循环遍历

语法:

index = 0										
while index < len(列表):
    元素 = 列表[index]

    # 对元素进行处理
    
    index += 1
1
2
3
4
5
6
7

举个栗子:

int_list = [1, 2, 3, 4]

index = 0
while index < len(int_list):
    item = int_list[index]				# 使用下标获取元素

    print(item)
    index += 1
1
2
3
4
5
6
7
8

执行结果:

1
2
3
4
1
2
3
4

for循环和while循环的区别:

while循环没有for循环简洁,但是while循环更灵活,可以根据条件,自行控制取出的元素,例如可以控制只遍历 index 为偶数位的元素,index += 2即可。for循环只能一个一个遍历元素,次数也是固定的。

# 10 遍历与删除

现在有个列表 [8, 20, 15, 5],现在想删除容器内大于10的元素。

于是,又要哐哐敲代码了:

num_list = [8, 20, 15, 5]

for num in num_list:
    if num > 10:
        num_list.remove(num)

print(num_list)
1
2
3
4
5
6
7

执行结果:

[8, 15, 5]
1

代码没问题啊,What the f**k,为什么呢?

其实遍历列表,底层还是通过下标来操作数据的,通过下标递增,来实现数据读取,当下标为1的时候,读取到20的时候,20 > 10,将20删掉了,那么后面的元素会向前移动,此时这一次的循环结束,下标加1,此时下标变成了2,而此时num_list[0] = 8,num_list[1] = 15,num_list[2] = 5,所以直接读取到5的,跳过了15。


那怎么在遍历的时候删除呢?可以采用倒序遍历。

从列表的最后一个元素开始遍历,这样删除元素不会影响未遍历的元素的索引。

num_list = [8, 20, 15, 5]

# 从最后一个开始向前遍历,步进为-1
for i in range(len(num_list) - 1, -1, -1):
    if num_list[i] > 10:
        num_list.pop(i)

print(num_list)
1
2
3
4
5
6
7
8

# 11 列表推导式

什么是列表推导式?

就是使用一种更简洁的方式,将可迭代对象转换为列表。

举个例子:

我想将一个列表中的数字都乘以10。于是写代码如下:

int_list = [1, 2, 3, 4]
int_list2 = []
for item in int_list:
    int_list2.append(item * 10)
print(int_list2)
1
2
3
4
5

可以使用列表推导式,让代码更简洁,语法如下:

变量 = [表达式 for 变量 in 可迭代对象]
变量 = [表达式 for 变量 in 可迭代对象 if 条件]
1
2

所以上面的代码可以改写成如下:

int_list = [1, 2, 3, 4]
int_list2 = [item * 10 for item in int_list]
print(int_list2)
1
2
3

如果只想偶数,则还可以添加条件判断:

int_list = [1, 2, 3, 4]
int_list2 = [item * 10 for item in int_list if item % 2 == 0]
print(int_list2)
1
2
3

执行结果:

[20, 40]
1

列表推导式用的不多,会让代码可读性变差,但是需要知道这种写法。

# 12 将列表拼接成字符串

可以使用 连接符.join(列表) 方法,可以将列表拼接成一个字符串。

举个栗子:

int_list = ["a", "b", "c", "d"]
str = "-".join(int_list)				# 使用-将列表中的元素拼接起来。
print(str)
1
2
3

执行结果:

a-b-c-d
1

# 13 将字符串分割成列表

可以使用分割符,将字符串分割成列表。

举个栗子:

str = "I love python"
str_list = str.split(" ")        # 使用空格将字符串分割成一个列表
print(str_list)
1
2
3

执行结果:

['I', 'love', 'python']
1

# 5.2 元组(tuple)

什么是元组?

元组和列表非常相似,和列表的区别就是:列表的元素可以被修改,元组在定义的时候就确定了元素,后面是不可以修改的。

如果我们要封装数据,又不希望数据被修改,就可以使用元组了。

# 1 元组的定义

元组使用 **小括号() **来定义,元组中的元素使用 逗号, 分隔。

# 定义一个列表,并赋值给一个变量
变量名称 = (元素1, 元素2, 元素3, ...)

# 定义一个元素的元组
变量名称 = (元素1, )

# 定义空列表
变量名称 = ()				# 方式1
变量名称 = tuple()		# 方式2
1
2
3
4
5
6
7
8
9

注意:定义包含一个元素的元组,后面必须带一个逗号。

因为元组中的元素是不能修改的,所以创建一个空元组好像也没有什么用,因为不能增加和删除元素。

# 2 元组的修改

因为元组中的元素是不能修改的,修改会报错。

int_tuple = (1, 2, 3)
int_tuple[0] = 5
1
2

执行结果:

Traceback (most recent call last):
File "****/hello_world.py", line 2, in <module>
int_tuple[0] = 5

TypeError: 'tuple' object does not support item assignment
1
2
3
4
5

再看下面的情况

int_list = [4, 5, 6]
int_tuple = (1, 2, 3, int_list)

print(int_tuple)
int_list.append(7)							# 修改元组中类型为列表的元素
print(int_tuple)
1
2
3
4
5
6

执行结果:

(1, 2, 3, [4, 5, 6])
(1, 2, 3, [4, 5, 6, 7])
1
2

不是说元组的元素不能修改吗,为什么现在又可以修改了。

其实元组中第4个元素始终指向的是列表的地址,没有变,只是列表中的元素变了,参见 4.6 容器的内存。

# 3 元组常用操作

元组元素不能修改,所以操作的方法比较少。

int_tuple = (1, 2, 3, 1)

print("获取元素:%d" % int_tuple[2])             				# 根据下表获取元素

print("获取元素2的位置:%d" % int_tuple.index(2))       	# 获取元素2的位置

print("元素1的个数:%d" % int_tuple.count(1))							# 获取元素1的个数

print("元组的长度:%d" % len(int_tuple))       						# 获取元组长度
1
2
3
4
5
6
7
8
9

元组的嵌套、遍历等操作都和列表相同,可以参照列表。

# 元组赋值给多个变量

可以将元组直接赋值给多个变量。

my_tuple = (1, 2)
a, b = my_tuple
print(a)
print(b)
1
2
3
4

执行结果:

1
2
1
2

需要注意,元组元素的个数需要和赋值的变量的个数相同,否则会报错。

# 5.3 字符串(Str)

前面已经讲解了字符串的很多知识,没想到我又来了。更没想到,字符串也是容器吧。

字符串是字符的容器,一个字符串可以存放任意数量的字符。

和列表一样,字符串也可以通过下标来访问其中的元素。

和元组一样,字符串中的元素不可以修改。

str = "Hello"
print(str[0])
print(str[-1])
1
2
3

执行结果:

H
o
1
2

和元组一样,字符串也是一个无法修改的容器,所以不能修改其中的字符,也不能移除和追加字符。

# 1 字符串长度

len() 函数可以获取字符串的长度。

举个栗子:

str = "Good good study, day day up"
print(len(str))
1
2

执行结果:

27
1

# 2 查找元素

和列表一样,使用 index() 方法查找字符在字符串中的下标。

str = "Good good study, day day up"
print(str.index("study"))
1
2

执行结果:

10
1

# 3 字符串替换

我们可以通过 replace() 方法替换字符串中的字符。

语法: 字符串.replace(字符串1, 字符串2) ,将字符串中全部的字符串1替换为字符串2

注意,替换完成,返回的是一个新的字符,旧的字符串是不变的,因为字符串是不可变的。

str = "Good good study, day day up"
new_str = str.replace("study", "play")
print(new_str)
print(str)
1
2
3
4

执行结果:

Good good play, day day up
Good good study, day day up
1
2

# 4 字符串分割

可以使用 split() 方法,通过指定字符分割字符串。

语法:字符串.split(分割符字符串),使用分割字符串讲字符串分割成多个字符串。

注意,字符串本身不变,得到的结果是一个字符串列表。

str = "Good good study, day day up"
str_list = str.split(" ")
print(str_list)
print(type(str_list))
1
2
3
4

执行结果:

['Good', 'good', 'study,', 'day', 'day', 'up']
<class 'list'>
1
2

# 5 规整操作

用户在登录系统的时候,输入用户名,用户名前后可能不小心输入了空格,我们在处理的时候,需要处理掉字符串前后的空格。

我们可以使用 strip() 方法对字符串进行规整。

举个栗子:

str = "   zhangsan   "
new_str = str.strip()
print(new_str)
1
2
3

执行结果:

zhangsan
1

strip() 方法还可以接收参数,去掉前后指定的字符串。

举个栗子:

str = "123zhangsan321"
new_str = str.strip("12")
print(new_str)

str = "123zhangsan132"
new_str = str.strip("21")
print(new_str)

str = "123zhangsan321"
new_str = str.strip("23")
print(new_str)
1
2
3
4
5
6
7
8
9
10
11

执行结果:

3zhangsan3
3zhangsan13
123zhangsan321
1
2
3

可以看到,参数是分开匹配的,当字符串最前或最后的一个字符在指定的参数中后,会继续匹配前后的下一个字符。

# 6 统计指定字符出现的数量

可以使用 字符串.count(元素) 方法 ,来统计字符串内,指定子串出现的数量。

举个栗子:

str = "Good good study, day day up"
print(str.count("day"))
1
2

执行结果:

2
1

# 7 字符串的遍历

和列表一样,字符串也可以使用while或for循环对字符进行遍历。

while 循环

str = "Hello"
index = 0
while index < len(str):
    print(str[index])
    index += 1
1
2
3
4
5

for循环

str = "Hello"
for s in str:
    print(s)
1
2
3

执行结果:

H
e
l
l
o
1
2
3
4
5

# 8 字符串大小比较

"abc" 和 "+-X/" 哪个字符串大呢?

str1 = "abc"
str2 = "+-X/"
print(str1 > str2)
1
2
3

执行结果:

True
1

那么字符串是怎么比较大小的呢?

字符串中的字符:大小写英文字符、数字、特殊字符在ASCII码中都有对应的数值,在进行字符串比较的时候,会依次取出两个字符串的字符进行比较。例如先取出两个字符串的第一个字符进行比较,哪个字符大,哪个字符串就大,如果相等,则取第二个字符进行比较,依次类推,直到比出最后的结果。

# 5.4 容器的切片

什么是切片?

切片就是截取容器中的一部分元素形成一个新的列表。

切片支持从指定位置开始,依次取出元素,到指定位置结束,得到一个新的序列。

所以容器必须是有序的序列,列表、元组、字符串都支持切片操作。

语法:

序列[起始下标:结束小标:步长]
1

起始下标表示从截取开始的位置,可以不写,不写表示从头开始;

结束下标表示截取结束的位置,也可以不写,不写表示截取到结尾。

步长表示取出元素的间隔

步长为1,表示一个一个取出元素,步长可以不写,不写默认为1;

步长为2,表示间隔一个元素取一个元素,以此类推;

步长可以为负数,表示倒序来取,此时起始下标和结束下标也需要反过来写。

注意:切片不会影响原来的系列,而是会得到一个新的序列。

举个栗子1:

num_list = [1, 2, 3, 4, 5]

new_list1 = num_list[1:4:1]
new_list2 = num_list[1:4]

print(new_list1)
print(new_list2)
1
2
3
4
5
6
7

从下标1开始,下标4结束(不包含该位置),步长为1

执行结果:

[2, 3, 4]
[2, 3, 4]
1
2

举个栗子2:

num_list = [1, 2, 3, 4, 5]
new_list = num_list[:4:2]
print(new_list)
1
2
3

从头开始,下标4结束,步长为2

执行结果:

[1, 3]

举个栗子3:

num_list = [1, 2, 3, 4, 5]
new_list = num_list[::2]
print(new_list)
1
2
3

从头开始,到末尾结束,步长为2

执行结果:

[1, 3, 5]

举个栗子4:

num_list = [1, 2, 3, 4, 5]
new_list = num_list[:]
print(new_list)
1
2
3

从头开始,到末尾结束,步长为1

执行结果:

[1, 2, 3, 4, 5]

举个栗子5:

num_list = [1, 2, 3, 4, 5]
new_list = num_list[3:1:-1]
print(new_list)
1
2
3

步长为-1,表示倒序来取,此时开始位置从下标为3的位置开始,结束位置下标为1(不包含)。

执行结果:

[4, 3]

举个栗子6:

num_list = (1, 2, 3, 4, 5)
new_list = num_list[:1:-2]
print(new_list)
1
2
3

序列是一个元组,步长为-2,此时开始位置表示序列的结尾开始,结束位置下标为1(不包括)。

执行结果:

(5, 3)

举个栗子7:

str = "Hello"
new_str = str[::-1]
print(new_str)
1
2
3

序列为字符串,步长为-1,此时开始位置表示序列的结尾开始,到序列的开始位置结束。

这种方式可以用来让字符串倒序。

执行结果:

olleH

# 5.5 容器的内存结构

下面以列表为例,讲解容器的内存结构,这样更利于我们理解某些操作的原理。

先看代码:

list1 = [1, 3, 5, 7]
1

在内存中的存储结构如下:

列表中并不是直接存储了元素,而是存储了元素的地址。

看下面的代码,list2的结果是?

list1 = [1, 3, 5, 7]
list2 = list1
list1 = [2, 4, 6]
print(list2)							# [1, 3, 5, 7]
1
2
3
4

执行完的内存结构图:

就是首先list2 = list1将list2指向了list1指向的列表,然后list1又指向了一个新的列表。

看下面的代码,list2的结果是?

list1 = [1, 3, 5, 7]
list2 = list1
list1[0] = 4
print(list2)							# [4, 3, 5, 7]
1
2
3
4

执行完的内存结构图:

首先list2 = list1将list2指向了list指向的集合,然后修改了集合中第一个元素的指向,创建了4,并将集合中的第一个元素指向了4。

看下面的代码,list2的结果是?

list1 = [1, 3, [4, 8]]
list2 = list1
list1[2][0] = 5
print(list2)						# [1, 3, [5, 8]]
1
2
3
4

执行完的内存结构图:

看下面的代码,list2的结果是?

list1 = [1, 3, 5, 7]
list2 = list1[:]
list1[2] = 4
print(list1)						# [1, 3, 4, 7]
print(list2)						# [1, 3, 5, 7]
1
2
3
4
5

执行完的内存结构图:

切片是重新创建了一个列表,但是列表中的元素是指向了原来数组元组指向的地址。

如果重新赋值,那么会重新指向新的地址。

看下面的代码,list2的结果是?

list1 = [1, 3, [4, 8]]
list2 = list1[:]
list1[2][0] = 5
print(list1)                    # [1, 3, [5, 8]]
print(list2)										# [1, 3, [5, 8]]
1
2
3
4
5

执行完的内存结构图:

切片是重新创建了一个列表,但是只会复制原来列表的第一层数据,更深层次的子列表,是不会复制的,两个列表指向的是相同的地址。

# 1 深拷贝

上面这种通过切片的方式复制容器形成一个新容器的方式为浅拷贝的方式,只能赋值容器的第一层数据;

还有一种是深拷贝的方式,就是拷贝一个容器形成另外一个容器,这两个容器就是两个完全独立的容器了。

下面介绍一下深拷贝的方式:

import copy													# 引入copy依赖

list1 = [1, 3, [4, 8]]
list2 = copy.deepcopy(list1)
list1[2][0] = 5
print(list1)                        # [1, 3, [5, 8]]
print(list2)												# [1, 3, [4, 8]]
1
2
3
4
5
6
7

首先引入copy包,然后使用deepcopy方法进行深拷贝。

执行完成的内存结构图如下:

这种方式就比较简单了,复制完成就完全是两个独立的容器了,两者之间不会出现任何关系。

这种深拷贝的方式在实际的开发中用的不多。

# 5.6 集合(set)

已经有列表和元组了,感觉已经很强大了,为什么还需要集合呢?

列表和元组都支持重复的元素,如果需要对内容进行去重,就显得有些不方便了。

集合更重要的是用来做一些运算,例如求两个容器的交集、并集、差集等。

集合最主要的特点:

  • 不支持重复的元素
  • 内容是无序的,不能下标来访问元素
  • 集合是可以被修改的
  • 可以存储不同的数据类型

# 1 集合的定义

使用 花括号{} 来定义集合。

# 定义一个集合,并赋值给一个变量
变量名称 = {元素1, 元素2, 元素3, ...}

# 定义空集合
变量名称 = set()
1
2
3
4
5

举个栗子:

my_set = {"Good" , "good", "study", "day", "day", "up"}
print(my_set)
1
2

执行结果:

{'Good', 'good', 'day', 'up', 'study'}

可以看出虽然在集合中添加了重复的元素,但是最终被去重了,而且可以看出集合是无序的。

# 2 集合的常用操作

# 集合长度

使用 len() 函数获取集合的长度。

my_set = {1, 2, 3, 4, 5}
print(len(my_set))
1
2

执行结果:

5

# 添加元素

使用 .add() 方法,可以将指定的元素,添加到集合内。

集合本身被修改,不会产生新集合。

my_set = {"I", "love"}
my_set.add("you")
print(my_set)
1
2
3

执行结果:

{'I', 'love', 'you'}

# 删除元素

使用 .remove() 方法,可以将指定的元素,从集合中移除。

集合本身被修改,不会产生新集合。

my_set = {"I", "love", "you"}
my_set.remove("you")
print(my_set)
1
2
3

执行结果:

{'love', 'I'}

# 取出元素

使用 .pop() 方法,可以从集合中随机取出一个元素。

集合本身被修改,不会产生新集合。

my_set = {"I", "love", "you"}
word = my_set.pop()
print(word)
print(my_set)
1
2
3
4

执行结果:

I {'love', 'you'}

每次执行的结果可能不一样。

# 清空集合

使用 .clear() 方法,可以将集合清空。

集合本身被修改,不会产生新集合。

my_set = {"I", "love", "you"}
my_set.clear();
print(my_set)
1
2
3

执行结果:

set()

结果变成了一个空集合。

# 交集

可以使用 & 运算符求出两个集合共有的元素。

set1 = {1, 2, 3, 4}
set2 = {1, 3, 5, 7}

set3 = set1.union(set2)
print(set3)

set4 = set1 | set2
print(set4)
1
2
3
4
5
6
7
8

执行结果:

{1, 2, 3, 4, 5, 7} {1, 2, 3, 4, 5, 7}

# 并集

可以使用 | 运算符或 集合1.union(集合2) 方法,获取集合1和集合2的并集,最终会得到一个新集合,集合1和集合2不变。

set1 = {1, 2, 3, 4}
set2 = {1, 3, 5, 7}
set3 = set1.union(set2)
print(set3)
1
2
3
4

执行结果:

{1, 2, 3, 4, 5, 7}

# 补集

可以使用 ^ 运算符求出两个集合的补集,补集也就是只在一个集合中存在的元素。

set1 = {1, 2, 3, 4}
set2 = {1, 3, 5, 7}

set3 = set1 ^ set2
print(set3)
1
2
3
4
5

执行结果:

{2, 4, 5, 7}

# 差集

使用 集合1.difference(集合2) 方法,可以得到集合1中存在而集合2中不存在的元素。

集合1和集合2不变,最终返回的是一个新的集合。

set1 = {1, 2, 3, 4}
set2 = {1, 3, 5, 7}
set3 = set1.difference(set2)
print(set3)
1
2
3
4

执行结果:

{2, 4}

# 差集_更新

使用 集合1.difference_update(集合2) 方法,可以删除集合1内和集合2内相同的元素。

集合1.difference(集合2) 方法很相似,但是会更新集合1,集合2不变。

set1 = {1, 2, 3, 4}
set2 = {1, 3, 5, 7}
set1.difference_update(set2)
print(set1)
1
2
3
4

执行结果:

{2, 4}

# 判断是否是子集或超集

可以使用 大于号>小于号< 来判断一个集合是否是另一个集合的子集或超集。

set1 = {1, 2, 3}
set2 = {1, 3}
print(f"set1是否是set2的超集: {set1 > set2}")
print(f"set2是否是set1的子集: {set2 < set1}")
1
2
3
4

执行结果:

set1是否是set2的超集: True set2是否是set1的子集: True

# 集合的遍历

因为集合是无序的,不支持下标索引,所以无法使用while循环来遍历。

for循环遍历:

my_set = {1, 2, 3, 4, 5}
for num in my_set:
    print(num)
1
2
3

执行结果:

1 2 3 4 5

# 5.7 字典(dict)

什么是字典?

字典和集合有一些相似,但是字段的存储数据是使用键值对,key-value的方式存储数据。

例如有一份数据:

姓名 成绩
zhangsan 94
lisi 96
wangwu 91

使用列表、集合的方式存储上面的数据是很不方便的。

而使用字典存储就很适合,可以将姓名作为key,成绩作为value。

{
  "zhangsan": 94,
  "lisi": 96,
  "wangwu": 91
}
1
2
3
4
5

这样可以很容易通过姓名key得到对应的成绩value。

# 5.7.1 字典的定义

字典同样是使用 花括号{} 来定义的,但是存储的元素是一个一个的键值对。

# 定义字典变量
变量名称 = {key1: value1, key2: value2, key3: value3, ...}

# 定义空字典
变量名称 = {}
1
2
3
4
5
  • key 和 value 使用冒号分隔,每一对使用逗号分隔

  • key 和 value可以是任意类型的数据,不过key不能是字典

  • key不可以重复,重复会覆盖已存在的数据

举个栗子:

stu_dict = {"zhangsan": 94, "lisi": 96, "wangwu": 91}
print(stu_dict)
1
2

执行结果:

{'zhangsan': 94, 'lisi': 96, 'wangwu': 91}

# 5.7.2 字典的嵌套

刚才说了 key 和 value 可以是任意数据类型,key不能为字典,说明字典是可以嵌套的。

例如我们要记录学生各科的成绩:

姓名 语文 数学 英语
zhangsan 86 91 79
lisi 87 88 89
wangwu 92 81 86

那么可以定义一下字典:

stu_dict = {
  "zhangsan": {"Chinese":86, "Math":91, "English": 79}, 
  "lisi": {"Chinese":87, "Math":88, "English": 89}, 
  "wangwu": {"Chinese":92, "Math":81, "English": 86}
}

1
2
3
4
5
6

key是可以使用汉字的,但是不建议。

# 5.7.3 字典的常用操作

# 1 获取元素的值

字典是无序的,所以不能通过下标来访问,可以使用 字典[key]字典.get(key) 方法通过key来获取value的值。

stu_dict = {
  "zhangsan": {"Chinese":86, "Math":91, "English": 79}, 
  "lisi": {"Chinese":87, "Math":88, "English": 89}, 
  "wangwu": {"Chinese":92, "Math":81, "English": 86}
}

print(stu_dict.get("zhangsan"))
print(stu_dict["zhangsan"])
print(stu_dict["zhangsan"]["Chinese"])
print(stu_dict.get("zhangsan").get("Chinese"))
1
2
3
4
5
6
7
8
9
10

执行结果:

{'Chinese': 86, 'Math': 91, 'English': 79} {'Chinese': 86, 'Math': 91, 'English': 79} 86 86

注意:当我们根据key获取值的时候,如果key在字典中不存在是会报错的。

所以我们在获取的时候,要先判断当前的key是否存在:

if "zhangsan" in stu_dict:				# 先判断有没有“zhangsan“的key
    print(stu_dict["zhangsan"])

if "zhaoliu" not in stu_dict:
    print("不存在赵六")
1
2
3
4
5

后面的操作很多都是需要判断key是否存在的, 否则会报错。

# 2 字典的长度

同样,获取字典内所有元素的数量也是使用 len 函数。

stu_dict = {
  "zhangsan": 94,
  "lisi": 96,
  "wangwu": 91
}

print(len(stu_dict))
1
2
3
4
5
6
7

执行结果:

3

# 3 新增或更新元素

可以使用 字典[key] = value 的方式新增或更新字典中的元素,字典中没有对应的key,为新增,有则为更新。

stu_dict = {
  "zhangsan": 94,
  "lisi": 96,
  "wangwu": 91
}

stu_dict["zhaoliu"] = 83				# 没有对应的key,为新增
stu_dict["zhangsan"] = 88				# 已经存在的key,为更新
print(stu_dict)
1
2
3
4
5
6
7
8
9

执行结果:

{'zhangsan': 88, 'lisi': 96, 'wangwu': 91, 'zhaoliu': 83}

# 4 删除元素

可以使用 字典.pop(key) 方法,可以根据key获取指定的value,并将指定key的数据删除。

stu_dict = {
  "zhangsan": 94,
  "lisi": 96,
  "wangwu": 91
}

score = stu_dict.pop("lisi")
print(score)
print(stu_dict)
1
2
3
4
5
6
7
8
9

执行结果:

96 {'zhangsan': 94, 'wangwu': 91}

也可以直接使用 del 函数删除元素:

stu_dict = {
  "zhangsan": 94,
  "lisi": 96,
  "wangwu": 91
}

del stu_dict["lisi"]
print(stu_dict)
1
2
3
4
5
6
7
8

执行结果:

{'zhangsan': 94, 'wangwu': 91}

# 5 清空字典

可以使用 字典.clear() 方法,清空字典。

stu_dict = {
  "zhangsan": 94,
  "lisi": 96,
  "wangwu": 91
}

stu_dict.clear()
print(stu_dict)
1
2
3
4
5
6
7
8

执行结果:

{}

# 6 获取全部的key

可以使用 字典.keys() 方法获取字典中所有的key。

stu_dict = {
  "zhangsan": 94,
  "lisi": 96,
  "wangwu": 91
}

keys = stu_dict.keys()
print(keys)

keys_list = list(keys)			# 可以使用list()函数,将返回的所以的key转换为列表
print(keys_list)
1
2
3
4
5
6
7
8
9
10
11

执行结果:

dict_keys(['zhangsan', 'lisi', 'wangwu'])

['zhangsan', 'lisi', 'wangwu']

同样也可以通过 字典.values() 来获取字典所有的value。

# 7 字典的遍历

因为字典是无序的,所以不支持使用 while 循环来遍历。所以只能使用for循环来遍历。

for循环遍历:

stu_dict = {
  "zhangsan": 94,
  "lisi": 96,
  "wangwu": 91
}

for item in stu_dict.items():							# 遍历所有元素
  print(f"key={item[0]}, value={item[1]}")
1
2
3
4
5
6
7
8

执行结果:

key=zhangsan, value=94 key=lisi, value=96 key=wangwu, value=91

我们也可以先需要使用 字典.keys() 方法获取字典中所有的key,然后再使用for循环遍历:

stu_dict = {
  "zhangsan": 94,
  "lisi": 96,
  "wangwu": 91
}

keys = stu_dict.keys()													# 首先获取到所有的key
for key in keys:																# 遍历所有的key
  print(f"key={key}, value:{stu_dict[key]}")		# 通过key获取valuezhi
1
2
3
4
5
6
7
8
9

首先获取到所有的key,然后使用for循环遍历所有的可以,在遍历的时候,通过key获取到所有的value值。

执行结果:

key=zhangsan, value:94 key=lisi, value:96 key=wangwu, value:91

# 5.7.4 总结:字典的特点

  • 可以容纳多个不同类型的数据
  • 每一份数据是key-value的方式存储,可以通过key获取到value
  • 不支持下标索引
  • 支持修改
  • 可以使用for循环遍历,不支持while循环遍历

# 5.8 各个容器对比

上面介绍了列表、元组、字符串、集合、字典,下面用表格来比较一下各个的特点,以及使用场景。

功能 列表 元组 字符串 集合 字典
元素数量 支持多个 支持多个 支持多个 支持多个 支持多个
元素类型 任意 任意 仅字符 任意 key:除字典外任意类型
value:任意
下标索引 支持 支持 支持 不支持 不支持
重复元素 支持 支持 支持 不支持 不支持
可修改 支持 不支持 不支持 支持 支持
是否有序
使用场景 可修改、可重复的
批量数据
不可修改、可重复的
批量数据
一串字符或文本数据 不可重复的批量数据 用key检索value的
数据场景

# 5.9 容器的通用功能

各个容器有不同的功能,但是他们都属于容器,有一些功能是通用的。

例如之前介绍的 len() 函数,以及for循环遍历,各个容器都是支持的。

下面再介绍一下常用的通用的功能方法。

# 1 max(容器)

max(容器) 函数可以获取容器内最大的元素。

my_list = [1, 2, 3, 4, 5]
my_tuple = (1, 2, 3, 4, 5)
my_str = "Hello"
my_set = {1, 2, 3, 4, 5}
my_dict = {1:1*2, 2:2*2, 3:3*2, 4:4*2, 5:5*2}

print(max(my_list))
print(max(my_tuple))
print(max(my_str))
print(max(my_set))
print(max(my_dict))
1
2
3
4
5
6
7
8
9
10
11

字典类型,会取出key最大的元素。

执行结果:

5 5 o 5 5

对应的还有 min(容器) 函数,可以获取容器内最小的元素。

同样还有一些转换的函数,可以实现容器类型的转换:

list(容器)函数:可以将容器转换为列表。

str(容器) 函数:可以将容器转换为字符串。

tuple(容器)函数:可以将容器转换为元组。

set(容器) 函数:可以将容器转换为集合。

# 2 容器排序

可以使用 sorted(容器, [reverse=True]) 函数,来对指定的容器中的元素进行排序,排序后会得到一个新的列表对象,原容器不变。

[reverse=True]可以省略。

my_list = [1, 8, 3, 6]
new_list = sorted(my_list)

print(my_list)
print(new_list)
1
2
3
4
5

执行结果:

[1, 8, 3, 6] [1, 3, 6, 8]

可以看出原容器是不变的,得到一个新的列表对象。

可以指定 reverse 参数,来实现倒序排列:

my_list = [1, 8, 3, 6]
new_list = sorted(my_list, reverse=True)

print(my_list)
print(new_list)
1
2
3
4
5

执行结果:

[1, 8, 3, 6] [8, 6, 3, 1]

对其他容器类型进行排序:

# 对元组进行排序
my_tuple = (1, 8, 3, 6)
new_list1 = sorted(my_tuple, reverse=True)
print(new_list1)

# 对字符串进行排序
my_str = "Hello"
new_list2 = sorted(my_str, reverse=True)
print(new_list2)

# 对集合进行排序
my_set = {1, 8, 3, 6}
new_list3 = sorted(my_set, reverse=True)
print(new_list3)

# 对字典进行排序
my_dict = {1:1*2, 2:2*2, 3:3*2, 4:4*2, 5:5*2}
new_list4 = sorted(my_dict, reverse=True)
print(new_list4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

执行结果:

[8, 6, 3, 1] ['o', 'l', 'l', 'e', 'H'] [8, 6, 3, 1] [5, 4, 3, 2, 1]

对字符串进行排序,会讲字符串拆分成一个一个字符进行排序,组成一个列表。

对字典进行排序,是对字典中所有的key进行排序。