一、集合(set)
1. 集合的特点
无序:元素没有索引,不能通过下标访问。
元素唯一:自动去重,重复元素只会保留一个。
可变:可以动态添加或删除元素。
元素必须可哈希(hashable):即元素必须是不可变类型(如
int、float、str、tuple、frozenset等)。
2. 创建集合
# 使用花括号 s1 = {1, 2, 3} # 使用 set() 构造函数(可迭代对象) s2 = set([1, 2, 2, 3]) # 自动去重 -> {1, 2, 3} s3 = set("hello") # {'h', 'e', 'l', 'o'} # 空集合只能用 set(),不能用 {}(那是空字典) empty_set = set()3. 常用方法
添加元素
s = {1, 2} s.add(3) # {1, 2, 3} s.update([4, 5]) # 添加多个元素 -> {1, 2, 3, 4, 5}删除元素
s.remove(3) # 移除指定元素,不存在则抛出 KeyError s.discard(4) # 移除指定元素,不存在也不报错 s.pop() # 随机移除并返回一个元素(集合为空时抛出 KeyError) s.clear() # 清空集合集合运算(方法 vs 运算符)
| 运算 | 方法 | 运算符 | 说明 |
|---|---|---|---|
| 并集 | a.union(b) | a │ b | 返回两个集合的并集 |
| 交集 | a.intersection(b) | a & b | 返回两个集合的交集 |
| 差集 | a.difference(b) | a - b | 返回在 a 但不在 b 的元素 |
| 对称差 | a.symmetric_difference(b) | a ^ b | 返回不同时属于 a 和 b 的元素 |
a = {1, 2, 3} b = {3, 4, 5} print(a | b) # {1,2,3,4,5} print(a & b) # {3} print(a - b) # {1,2} print(a ^ b) # {1,2,4,5}拷贝
s = {1, 2, 3} s2 = s.copy() # 浅拷贝注意:集合的浅拷贝对于元素本身是引用类型(如自定义对象)时,内部对象仍是共享的。但由于集合元素必须可哈希,大部分基本类型不可变,所以实际影响较小。
4. 集合推导式
s = {x**2 for x in range(5)} # {0, 1, 4, 9, 16}frozenset 是 Python 内置的不可变集合。一旦创建,就不能添加、删除或修改任何元素。因此它可哈希,可以作为字典的键,也可以作为另一个集合的元素。a = frozenset([1, 2, 3]) b = frozenset([3, 4, 5]) print(a | b) # frozenset({1,2,3,4,5}) print(a & b) # frozenset({3}) print(a - b) # frozenset({1,2}) print(a.union(b)) # 同上 print(a.issubset(b)) # False二、注意事项与常见误区
1. 元素必须可哈希
s = {[1,2]} # TypeError: unhashable type: 'list' s = {{1:2}} # TypeError: unhashable type: 'dict'2. 元组内的元素如果可变,整个元组也不可哈希
t = (1, [2,3]) s = {t} # TypeError: unhashable type: 'list'3. 浅拷贝与深拷贝的区别(针对集合)
当集合内元素是可变对象时,浅拷贝会让内部对象共享。但由于集合元素必须可哈希,这类场景较少见。若使用自定义类(实现了哈希),浅拷贝后修改对象内部状态会影响原集合中的对象。
三、总结
集合是高效的去重与关系运算工具,适合处理无序、不重复的元素集合。
冻结集合是不可变版本,牺牲了修改能力,换来了可作为键或集合元素的灵活性。
#集合的常用方法 #1.添加元素 s1 = {1,2,3,4,5,6} s1.add(999) print(f"s1:{s1}") #2.删除元素 s1.remove(3) print(f"s1:{s1}") #3.清空集合 s1.clear() print(f"s1:{s1}") #4.集合的交集 s2 = {2,5,6,7} s1.add(2) s1.add(3) s1.add(4) s1.add(5) s1.add(6) print(f"s1:{s1}") print(f"s2:{s2}") s3 = s1 & s2 print(f"s3:{s3}") #5.集合的并集 s4 = s1 | s2 print(f"s4:{s4}") #6.集合的差集 s5 = s1 - s2 #差集s1 - s2就是s1中有而s2中没有的元素 print(f"s5:{s5}") #7.集合的对称差集 s6 = s1 ^ s2 #对称差集s1 ^ s2就是s1和s2中不同时存在的元素 print(f"s6:{s6}") #8.集合的子集和超集 s7 = {2,5} print(f"s7:{s7}") print(f"s7是否是s1的子集:{s7.issubset(s1)}")#issubset方法用于判断一个集合是否是另一个集合的子集,如果是则返回True,否则返回False。 print(f"s1是否是s7的超集:{s1.issuperset(s7)}")#issuperset方法用于判断一个集合是否是另一个集合的超集,如果是则返回True,否则返回False。超集就是包含另一个集合的集合。 #9.集合的长度 print(f"s1的长度:{len(s1)}")#len函数用于返回集合中元素的个数。 #10.集合的成员资格测试 print(f"2是否在s1中:{2 in s1}")#in运算符用于测试一个元素是否在集合中,如果在则返回True,否则返回False。 print(f"10是否在s1中:{10 in s1}")#in运算符用于测试一个元素是否在集合中,如果在则返回True,否则返回False。 #11.集合的迭代 print("s1中的元素:") for element in s1: print(element) #12.集合的复制 s8 = s1 print(f"s8:{s8}")#直接赋值 s2 = s1 只是引用传递,不是复制,改一个会影响另一个。 s8.add(888) print(f"s8:{s8}") print(f"s1:{s1}") #深拷贝和浅拷贝 #浅拷贝 s9 = s1.copy()#copy方法用于创建一个集合的浅拷贝,返回一个新的集合对象,包含原集合中的元素。 print(f"s9:{s9}") s9.add(777) print(f"s9:{s9}") print(f"s1:{s1}")#浅拷贝修改s9不会影响s1,因为s9是s1的一个新的集合对象。 #浅拷贝对于多层嵌套的集合来说,内部的元素仍然是共享的,如果修改了内部的元素,可能会影响到原集合。 s10 = {1,2,3,(4,5,9)}#集合中的元素必须是不可变类型,所以元组可以作为集合的元素。 # s10[3].append(10)#元组是不可变类型,不能修改,所以会报错。 #浅拷贝和深拷贝对于集合来说,copy方法是浅拷贝,如果集合中包含可变类型的元素,那么修改这些元素会影响到原集合。 #深拷贝 import copy s11 = copy.deepcopy(s1)#deepcopy函数用于创建一个集合的深拷贝,返回一个新的集合对象,包含原集合中的元素,并且对于可变类型的元素也会创建新的对象。 print(f"s11:{s11}") s11.add(666) print(f"s11:{s11}") print(f"s1:{s1}")#深拷贝修改s11不会影响s1,因为s11是s1的一个新的集合对象,并且对于可变类型的元素也会创建新的对象。 #冻结集合 s12 = {frozenset([1,2,3]),frozenset([3,6])} print(f"s12:{s12}") print(f"type(s12):{type(s12)}")#集合中的元素必须是不可变类型,所以frozenset可以作为集合的元素。 print(len(s12))#len函数用于返回集合中元素的个数。