❝Python入门第十课,主要学习最重要的数据容器——集合,集合是无序且元素唯一的数据容器,支持数学集合运算,是去重和关系判断的核心工具。
什么是集合
集合是一种无序、元素唯一的容器类型。
无序,是指从集合中取出元素的顺序,与定义集合时存入的顺序不一定一致。
集合分两种,分别是:
- 可变集合(set):内部的元素无序(不保证顺序),不能通过下标访问元素、会自动去除重复元素。
- 不可变集合(frozenset):特点和可变集合一样,唯一的区别是:其中的元素不可改变。
定义集合
定义可变集合
可变集合使用花括号{}包裹,不同数据项之间用,做分隔。
# 定义非空可变集合s1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}s2 = {'你好', '远方', '太原'}s3 = {100, '优秀', True, 50.89, None}print(type(s1), s1) # <class 'set'> {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}print(type(s2), s2) # <class 'set'> {'远方', '太原', '你好'}print(type(s3), s3) # <class 'set'> {None, True, 50.89, 100, '优秀'}# 定义可变空集合s4 = set()print(type(s4), s4) # <class 'set'> set()
需要注意的是,不能直接写{}来定义空集合,因为直接写{}定义的是空字典。
s5 = {}print(type(s5), s5) # <class 'dict'> {}
定义不可变集合
不可变集合需要借助内置的frozenset函数。
frozenset()函数接收的参数,可以是任意可迭代对象,但返回的一定是【不可变集合】。
# 定义非空不可变集合s6 = frozenset({10, 20, 30})s7 = frozenset({'你好', 'Python', '远方', '太原'})s8 = frozenset({100, '好好学习', True, 12.4, None})print(type(s6), s6) # <class 'frozenset'> frozenset({10, 20, 30})print(type(s7), s7) # <class 'frozenset'> frozenset({'太原', '你好', 'Python', '远方'})print(type(s8), s8) # <class 'frozenset'> frozenset({'好好学习', True, None, 100, 12.4})# frozenset 可接收可迭代对象,返回不可变集合s9 = frozenset([1, 2, 3])s10 = frozenset((10, 20, 30))s11 = frozenset('python')print(type(s9), s9) # <class 'frozenset'> frozenset({1, 2, 3})print(type(s10), s10) # <class 'frozenset'> frozenset({10, 20, 30})print(type(s11), s11) # <class 'frozenset'> frozenset({'y', 'p', 'n', 't', 'h', 'o'})# 定义不可变空集合s12 = frozenset()print(type(s12), s12) # <class 'frozenset'> frozenset()
集合嵌套
集合中不能嵌套可变集合,但可以嵌套不可变集合。(为什么?——只有“不可变”的东西,才能安全的放进集合里。)
s1 = {10, 20, 30}s2 = frozenset({100, 200, 300})l1 = [1000, 2000, 3000]t1 = ('a', 'b', 'c')# ss1 = {11, 22, 33, s1} # 报错:TypeError: unhashable type: 'set'ss2 = {11, 22, 33, s2}print(type(ss2), ss2) # <class 'set'> {33, frozenset({200, 100, 300}), 11, 22}# ss3 = {11, 22, 33, l1} # 报错:TypeError: unhashable type: 'list'ss4 = {11, 22, 33, t1}print(type(ss4), ss4) # <class 'set'> {33, 11, ('a', 'b', 'c'), 22}
增删改查
新增
方式1:使用集合.add(元素),向集合中添加元素,无返回值。
s1 = {1, 2, 3}s1.add(4)print(s1) # {1, 2, 3, 4}
方式2:使用集合.update(元素),向集合中批量添加元素(接收可迭代对象),无返回值。update 方法向集合中添加元素必须传递可迭代对象,例如:列表、元组、集合等。
s1 = {1, 2, 3}s1.update([4, 5])s1.update((6, 7))s1.update({8, 9})s1.update(range(10, 12))s1.update('abc')print(s1) # {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 'a', 'b', 'c'}
删除
方式1:使用集合.remove(元素),从集合中移除指定元素(若元素不存在,会报错),无返回值。
s1 = {0, 1, 2, 3, 5, 6, 7, 8, 9}s1.remove(5)print(s1) # {0, 1, 2, 3, 6, 7, 8, 9}
方式2:使用集合.discard(元素),从集合中移除指定元素(元素若不存在,不会报错),无返回值。
s1 = {0, 1, 2, 3, 5, 6, 7, 8, 9}s1.discard(5)print(s1) #
方式3:使用集合.pop(),从集合中移除任意一个元素,返回值:移除的那个元素。
s1 = {0, 1, 2, 3, 5, 6, 7, 8, 9}result = s1.pop()print(result, s1) # 0 {1, 2, 3, 5, 6, 7, 8, 9}result = s1.pop()print(result, s1) # 1 {2, 3, 5, 6, 7, 8, 9}
方式4:使用集合.clear(),清空集合,无返回值。
s1 = {0, 1, 2, 3, 5, 6, 7, 8, 9}result = s1.clear()print(result, s1) # None set()
修改
**注意**:集合没有下标,也不支持 replace 方法,所以集合没有专门用于“改”的方法,但可以使用 remove + add 的组合,来达到“修改”的效果。
s1 = {0, 1, 2, 3, 6, 5}print(s1) # {0, 1, 2, 3, 5, 6}s1.remove(5)s1.add(4)print(s1) # {0, 1, 2, 3, 4, 6}
查询
**注意**:由于集合没有下标,也不支持切片,所以集合不具备按位置访问的能力。虽然不能通过下标读取元素,但可以使用【成员运算符】来判断:某个元素是否在集合中,成员运算符我们会在后续详讲。
s1 = {1, 2, 3}result = 4in s1print(result) # Falseresult = 2in s1print(result) # True
常用方法
集合常用方法有如下几个:(下表中s1和s2代表两个内容不同的集合)
使用s1.difference(s2),找出s1中不同于s2的元素,s1和s2都不变,返回新集合。
s1 = {'a', 'b', 'c', 'd'}s2 = {'c', 'd', 'e', 'f', 'g'}result = s1.difference(s2)print(result) # {'b', 'a'}
使用s1.difference_update(s2),从s1中删除s2中存在的元素,s1被修改,s2不变,无返回值。
s1 = {'a', 'b', 'c', 'd'}s2 = {'c', 'd', 'e', 'f', 'g'}s1.difference_update(s2)print(s1) # {'b', 'a'}print(s2) # {'f', 'd', 'g', 'c', 'e'}
使用s1.union(s2),合并两个集合s1和s2,s1和s2都不变,返回新集合。
s1 = {'a', 'b', 'c', 'd'}s2 = {'c', 'd', 'e', 'f', 'g'}result = s1.union(s2)print(s1) # {'b', 'd', 'a', 'c'}print(s2) # {'e', 'd', 'g', 'f', 'c'}print(result) # {'e', 'b', 'd', 'g', 'f', 'a', 'c'}
使用s1.issubset(s2),判断s1是否为s2的子集,返回布尔值。如果集合s1的所有元素都在s2中,那就返回True,否则返回False。
s1 = {'a', 'b', 'c', 'd'}s2 = {'c', 'd', 'e', 'f', 'g'}s3 = {'c', 'd', 'e'}print(s3.issubset(s1)) # Falseprint(s3.issubset(s2)) # True
使用s1.issuperset(s2),判断s1是否为s2的超集,返回布尔值。如果集合s1中包含了集合s2的所有元素,那就返回True,否则返回False。
s1 = {'a', 'b', 'c', 'd'}s2 = {'c', 'd', 'e', 'f', 'g'}s3 = {'c', 'd', 'e'}print(s1.issuperset(s3)) # Falseprint(s2.issuperset(s3)) # True
使用s1.isdisjoint(s2),判断s1和s2是否没有交集,返回布尔值。如果没有交集,返回True;只要有一个公共元素,就返回False。
s1 = {'a', 'b', 'c', 'd'}s2 = {'c', 'd', 'e', 'f', 'g'}s3 = {'c', 'd', 'e'}s4 = {'x', 'y', 'z'}print(s1.isdisjoint(s3)) # Falseprint(s2.isdisjoint(s4)) # True
数据运算
并集
由两个集合中所有元素(重复元素只计一次)组成的集合。
s1 = {10, 20, 30, 40, 50, 60}s2 = {40, 50, 60, 70, 80, 90}result = s1 | s2print(result, type(result), len(result)) # {70, 40, 10, 80, 50, 20, 90, 60, 30} <class 'set'> 9
交集
由两个集合中共有的元素组成的集合。
s1 = {10, 20, 30, 40, 50, 60}s2 = {40, 50, 60, 70, 80, 90}result = s1 & s2print(result, type(result), len(result)) # {40, 50, 60} <class 'set'> 3
差集
由属于第一个集合但不属于第二个集合的元素组成的集合。
s1 = {10, 20, 30, 40, 50, 60}s2 = {40, 50, 60, 70, 80, 90}result = s1 - s2print(result, type(result), len(result)) # {10, 20, 30} <class 'set'> 3result = s2 - s1print(result, type(result), len(result)) # {80, 90, 70} <class 'set'> 3
对称差集
由恰好属于两个集合中一个的元素组成的集合(即并集减去交集)。
s1 = {10, 20, 30, 40, 50, 60}s2 = {40, 50, 60, 70, 80, 90}result1 = s1 ^ s2print(result1, type(result1), len(result1)) # {70, 10, 80, 20, 90, 30} <class 'set'> 6
循环遍历
由于集合不支持下标,所以集合不能使用 while 循环遍历。可以使用 for 循环遍历。
s1 = {1, 2, 3, 4, 5, 6}for item in s1: print(item)
特点总结
- 无序:集合中的元素没有固定顺序,无法通过下标访问。
- 分为两种:可变集合(set)和不可变集合(frozenset)。
- 集合中的元素必须是不可变类型(如:数字、字符串、元组)。
一句话总结:集合是可以去重的数据容器,当只关心元素是否存在,而不在乎顺序时,首选集合。