目 录CONTENT

文章目录

Python(二十三) 集合 set 详解

Python(二十三) 集合 set 详解

1. 什么是集合 set

在 Python 中,set 是一种用来存放多个元素的数据类型,中文通常叫做“集合”。

可以把集合理解成一个“装东西的袋子”,但这个袋子有两个非常重要的特点:

  1. 里面的元素不会重复。
  2. 里面的元素没有固定顺序。

例如:

s = {1, 2, 3, 3, 4}
print(s)

输出结果可能是:

{1, 2, 3, 4}

虽然代码里写了两个 3,但集合会自动去掉重复元素。

所以:

{1, 2, 3, 3}

实际保存的是:

{1, 2, 3}

2. 集合的核心特点

集合有几个非常重要的特点:

特点 说明
不重复 集合中相同的元素只会保留一个
无序 集合中的元素没有固定位置
可修改 可以向集合中添加或删除元素
查询快 判断某个元素是否在集合中通常很快
元素有限制 集合中的元素必须是不可变类型

2.1 不重复

集合最大的特点就是自动去重。

nums = {10, 20, 20, 30, 30, 30}
print(nums)

结果:

{10, 20, 30}

这个特点常用于处理重复数据。

2.2 无序

集合中的元素没有固定顺序。

s = {"Python", "Java", "C++"}
print(s)

输出结果不一定按照你写入的顺序显示。

所以,集合不能像列表一样通过下标访问元素。

s = {1, 2, 3}
print(s[0])  # 错误

这段代码会报错,因为集合没有“第 0 个元素”的概念。

2.3 可修改

集合本身是可变的,可以添加、删除元素。

s = {1, 2, 3}
s.add(4)
print(s)

结果:

{1, 2, 3, 4}

2.4 元素必须是不可变类型

集合中的元素必须是不可变类型,也就是可以被 Python 安全识别和比较的对象。

可以放入集合的常见类型:

s = {1, 3.14, "hello", True, (1, 2)}

不能放入集合的常见类型:

s = {[1, 2], [3, 4]}  # 错误

原因是列表是可变的,不能作为集合元素。

再比如:

s = {{1, 2}, {3, 4}}  # 错误

原因是集合本身也是可变的,所以普通集合不能作为另一个集合的元素。

如果确实需要把一个“集合”放进另一个集合,可以使用 frozenset

s = {frozenset({1, 2}), frozenset({3, 4})}
print(s)

3. 如何创建集合

3.1 使用大括号创建集合

s = {1, 2, 3}
print(s)

结果:

{1, 2, 3}

这种方式最常见。

3.2 使用 set() 创建集合

s = set([1, 2, 2, 3])
print(s)

结果:

{1, 2, 3}

set() 可以把列表、元组、字符串等可迭代对象转换成集合。

s1 = set([1, 2, 3])
s2 = set((1, 2, 3))
s3 = set("hello")

print(s1)
print(s2)
print(s3)

可能输出:

{1, 2, 3}
{1, 2, 3}
{'h', 'e', 'l', 'o'}

注意:字符串 "hello" 中有两个 l,转换成集合后只保留一个。

3.3 创建空集合

创建空集合必须使用 set()

s = set()
print(s)
print(type(s))

结果:

set()
<class 'set'>

注意,不能用 {} 创建空集合。

s = {}
print(type(s))

结果:

<class 'dict'>

{} 创建的是空字典,不是空集合。

4. 集合的基本操作

4.1 添加单个元素:add()

使用 add() 可以向集合中添加一个元素。

s = {"张三", "李四"}
s.add("王五")
print(s)

结果:

{'张三', '李四', '王五'}

如果添加的元素已经存在,集合不会重复保存,也不会报错。

s = {1, 2, 3}
s.add(2)
print(s)

结果:

{1, 2, 3}

4.2 批量添加元素:update()

使用 update() 可以一次添加多个元素。

s = {1, 2}
s.update([3, 4, 5])
print(s)

结果:

{1, 2, 3, 4, 5}

update() 接收的是一个可迭代对象,例如列表、元组、字符串、集合等。

s = {1, 2}
s.update("abc")
print(s)

可能输出:

{1, 2, 'a', 'b', 'c'}

这里要注意,字符串会被拆成一个个字符加入集合。

4.3 删除元素:remove()

使用 remove() 可以删除指定元素。

s = {1, 2, 3}
s.remove(2)
print(s)

结果:

{1, 3}

但是,如果删除的元素不存在,remove() 会报错。

s = {1, 2, 3}
s.remove(4)  # 报错

4.4 删除元素:discard()

discard() 也可以删除指定元素。

s = {1, 2, 3}
s.discard(2)
print(s)

结果:

{1, 3}

remove() 不同的是,如果元素不存在,discard() 不会报错。

s = {1, 2, 3}
s.discard(4)
print(s)

结果:

{1, 2, 3}

教学时可以这样总结:

remove(): 元素不存在会报错
discard(): 元素不存在不会报错

如果不确定元素是否存在,推荐使用 discard()

4.5 随机删除一个元素:pop()

使用 pop() 可以从集合中删除并返回一个元素。

s = {1, 2, 3}
x = s.pop()

print(x)
print(s)

因为集合是无序的,所以不能把 pop() 理解成“删除最后一个元素”。

它删除的是集合中的某个元素。

注意:如果集合为空,调用 pop() 会报错。

s = set()
s.pop()  # 报错

4.6 清空集合:clear()

使用 clear() 可以删除集合中的所有元素。

s = {1, 2, 3}
s.clear()
print(s)

结果:

set()

4.7 获取集合长度:len()

使用 len() 可以获取集合中元素的个数。

s = {"张三", "李四", "王五"}
print(len(s))

结果:

3

由于集合会自动去重,所以 len() 获取的是去重后的元素个数。

s = {1, 1, 2, 2, 3}
print(len(s))

结果:

3

4.8 判断元素是否存在

可以使用 in 判断某个元素是否在集合中。

students = {"张三", "李四", "王五"}

print("张三" in students)
print("赵六" in students)

结果:

True
False

也可以使用 not in 判断某个元素是否不在集合中。

students = {"张三", "李四", "王五"}

print("赵六" not in students)

结果:

True

集合非常适合用来做“是否存在”的判断。

例如,判断某个用户名是否已经注册:

registered_users = {"alice", "bob", "tom"}

username = "alice"

if username in registered_users:
    print("用户名已存在")
else:
    print("用户名可以使用")

5. 集合的遍历

集合可以使用 for 循环遍历。

s = {"Python", "Java", "C++"}

for item in s:
    print(item)

但是要注意,集合是无序的,遍历顺序不一定和写入顺序一致。

如果想按照固定顺序输出,可以先使用 sorted() 排序。

s = {3, 1, 2}

for item in sorted(s):
    print(item)

结果:

1
2
3

6. 集合的常见用途

6.1 去重

集合最常见的用途之一就是去重。

例如,有一个列表,里面有重复数字:

nums = [1, 2, 2, 3, 3, 3, 4]
unique_nums = set(nums)

print(unique_nums)

结果:

{1, 2, 3, 4}

如果还需要列表格式,可以再转回列表。

nums = [1, 2, 2, 3, 3, 3, 4]
unique_nums = list(set(nums))

print(unique_nums)

可能输出:

[1, 2, 3, 4]

但是要注意,使用 set() 去重可能会打乱原来的顺序。

6.2 去重并保留原顺序

如果希望去重后仍然保留原来的顺序,可以使用 dict.fromkeys()

nums = [3, 1, 2, 3, 1, 4]
result = list(dict.fromkeys(nums))

print(result)

结果:

[3, 1, 2, 4]

这种方式常用于“既要去重,又要保留原列表顺序”的场景。

6.3 快速判断重复数据

比如,判断一个列表中是否有重复元素。

nums = [1, 2, 3, 4, 4]

if len(nums) == len(set(nums)):
    print("没有重复元素")
else:
    print("有重复元素")

结果:

有重复元素

思路是:

如果列表长度和去重后的集合长度一样,说明没有重复。
如果长度不一样,说明有重复。

6.4 快速查找

集合适合用来判断某个数据是否存在。

blacklist = {"user001", "user002", "user003"}

user = "user002"

if user in blacklist:
    print("该用户在黑名单中")
else:
    print("该用户不在黑名单中")

当数据量很大时,使用集合判断成员关系通常比使用列表更快。

7. 集合运算

集合最强大的地方是支持数学中的集合运算,比如交集、并集、差集、对称差集。

下面用两个班级报名兴趣课的例子来讲解。

python_class = {"张三", "李四", "王五", "赵六"}
java_class = {"李四", "王五", "钱七", "孙八"}

python_class 表示报名 Python 课的学生。

java_class 表示报名 Java 课的学生。

7.1 交集

交集表示两个集合中共同拥有的元素。

也就是:两个班都报名的学生。

写法一:使用 &

both = python_class & java_class
print(both)

写法二:使用 intersection()

both = python_class.intersection(java_class)
print(both)

结果:

{'李四', '王五'}

图示理解:

Python 班:张三、李四、王五、赵六
Java 班:        李四、王五、钱七、孙八

交集:          李四、王五

7.2 并集

并集表示两个集合合并后的所有元素,重复元素只保留一个。

也就是:报名过任意一门课的所有学生。

写法一:使用 |

all_students = python_class | java_class
print(all_students)

写法二:使用 union()

all_students = python_class.union(java_class)
print(all_students)

结果:

{'张三', '李四', '王五', '赵六', '钱七', '孙八'}

图示理解:

Python 班:张三、李四、王五、赵六
Java 班:        李四、王五、钱七、孙八

并集:    张三、李四、王五、赵六、钱七、孙八

7.3 差集

差集表示一个集合中有,而另一个集合中没有的元素。

例如:

only_python = python_class - java_class
print(only_python)

或者:

only_python = python_class.difference(java_class)
print(only_python)

结果:

{'张三', '赵六'}

意思是:只报名 Python 课,没有报名 Java 课的学生。

如果反过来:

only_java = java_class - python_class
print(only_java)

结果:

{'钱七', '孙八'}

意思是:只报名 Java 课,没有报名 Python 课的学生。

注意:

python_class - java_class

和:

java_class - python_class

结果通常是不一样的。

差集要看方向。

7.4 对称差集

对称差集表示只属于其中一个集合,但不同时属于两个集合的元素。

也就是:只报名了一门课的学生。

写法一:使用 ^

only_one = python_class ^ java_class
print(only_one)

写法二:使用 symmetric_difference()

only_one = python_class.symmetric_difference(java_class)
print(only_one)

结果:

{'张三', '赵六', '钱七', '孙八'}

图示理解:

Python 班:张三、李四、王五、赵六
Java 班:        李四、王五、钱七、孙八

同时报名两门课:李四、王五
只报名一门课:  张三、赵六、钱七、孙八

7.5 集合运算总结表

运算 符号写法 方法写法 含义
交集 a & b a.intersection(b) 两个集合共有的元素
并集 a | b a.union(b) 两个集合所有元素合并
差集 a - b a.difference(b) a 中但不在 b
对称差集 a ^ b a.symmetric_difference(b) 只在其中一个集合中的元素

8. 修改原集合的集合运算

前面介绍的 intersection()union()difference() 等方法通常会产生一个新集合,不会直接修改原来的集合。

例如:

a = {1, 2, 3}
b = {3, 4, 5}

c = a & b

print(a)
print(c)

结果:

{1, 2, 3}
{3}

a 本身没有被修改。

如果希望直接修改原集合,可以使用下面这些方法。

8.1 intersection_update()

保留交集,并修改原集合。

a = {1, 2, 3}
b = {3, 4, 5}

a.intersection_update(b)

print(a)

结果:

{3}

8.2 difference_update()

删除另一个集合中也有的元素。

a = {1, 2, 3}
b = {3, 4, 5}

a.difference_update(b)

print(a)

结果:

{1, 2}

8.3 symmetric_difference_update()

保留只属于其中一个集合的元素,并修改原集合。

a = {1, 2, 3}
b = {3, 4, 5}

a.symmetric_difference_update(b)

print(a)

结果:

{1, 2, 4, 5}

8.4 update()

update() 可以看成是“把并集结果更新到原集合中”。

a = {1, 2, 3}
b = {3, 4, 5}

a.update(b)

print(a)

结果:

{1, 2, 3, 4, 5}

9. 集合关系判断

除了集合运算,Python 还可以判断集合之间的关系。

9.1 子集:issubset()

如果一个集合中的所有元素都在另一个集合中,那么它就是另一个集合的子集。

a = {1, 2}
b = {1, 2, 3, 4}

print(a.issubset(b))

结果:

True

也可以使用符号:

print(a <= b)

9.2 真子集:<

如果 ab 的子集,并且 ab 不相等,那么 a 就是 b 的真子集。

a = {1, 2}
b = {1, 2, 3}

print(a < b)

结果:

True

9.3 超集:issuperset()

如果一个集合包含另一个集合中的所有元素,那么它就是另一个集合的超集。

a = {1, 2, 3, 4}
b = {1, 2}

print(a.issuperset(b))

结果:

True

也可以使用符号:

print(a >= b)

9.4 真超集:>

a = {1, 2, 3}
b = {1, 2}

print(a > b)

结果:

True

9.5 判断两个集合是否没有交集:isdisjoint()

如果两个集合没有任何共同元素,isdisjoint() 返回 True

a = {1, 2, 3}
b = {4, 5, 6}

print(a.isdisjoint(b))

结果:

True

如果有共同元素:

a = {1, 2, 3}
b = {3, 4, 5}

print(a.isdisjoint(b))

结果:

False

10. 集合推导式

集合推导式和列表推导式很像,只是使用大括号 {}

10.1 生成平方数集合

s = {x * x for x in range(5)}
print(s)

结果:

{0, 1, 4, 9, 16}

10.2 筛选偶数并去重

nums = [1, 2, 2, 3, 4, 4, 5, 6]
evens = {x for x in nums if x % 2 == 0}

print(evens)

结果:

{2, 4, 6}

10.3 提取字符串中的不同字符

text = "banana"
chars = {ch for ch in text}

print(chars)

可能输出:

{'b', 'a', 'n'}

11. set 和 list 的区别

对比项 list 列表 set 集合
是否有序 有序 无序
是否允许重复 允许 不允许
是否支持下标 支持 不支持
常见用途 保存一组有顺序的数据 去重、集合运算、快速判断存在
示例 [1, 2, 2, 3] {1, 2, 3}

11.1 什么时候用 list

如果你关心顺序,或者需要通过下标访问元素,适合使用列表。

students = ["张三", "李四", "王五"]
print(students[0])

结果:

张三

11.2 什么时候用 set

如果你不关心顺序,只关心元素是否重复,或者需要快速判断元素是否存在,适合使用集合。

students = {"张三", "李四", "王五"}

if "张三" in students:
    print("找到了")

12. set 和 dict 的关系

集合 set 和字典 dict 都使用大括号。

s = {1, 2, 3}
d = {"name": "张三", "age": 18}

区别是:

类型 写法 说明
set {1, 2, 3} 只有元素
dict {"name": "张三"} 有键和值

空大括号 {} 表示字典。

empty = {}
print(type(empty))

结果:

<class 'dict'>

空集合必须写成:

empty_set = set()

13. frozenset:不可变集合

普通集合 set 是可变的。

s = {1, 2, 3}
s.add(4)
print(s)

结果:

{1, 2, 3, 4}

frozenset 是不可变集合,创建后不能添加或删除元素。

fs = frozenset([1, 2, 3])
print(fs)

结果:

frozenset({1, 2, 3})

不能这样写:

fs.add(4)  # 错误

frozenset 可以作为集合中的元素。

s = {frozenset({1, 2}), frozenset({3, 4})}
print(s)

普通 set 不可以作为集合中的元素,因为它是可变的。

14. 常见错误和注意事项

14.1 空集合不能写成 {}

错误理解:

s = {}

这不是空集合,而是空字典。

正确写法:

s = set()

14.2 集合是无序的,不能依赖输出顺序

s = {3, 1, 2}
print(s)

不要假设输出一定是:

{3, 1, 2}

集合的重点不是顺序,而是“是否存在”和“是否重复”。

14.3 集合不能使用下标

错误写法:

s = {1, 2, 3}
print(s[0])

集合不支持下标访问。

如果确实需要下标,可以先转换成列表。

s = {1, 2, 3}
lst = list(s)
print(lst[0])

但是转换后的顺序不一定和原来写入的顺序一致。

14.4 remove() 删除不存在的元素会报错

s = {1, 2, 3}
s.remove(4)  # 报错

如果不确定元素是否存在,使用 discard() 更安全。

s = {1, 2, 3}
s.discard(4)  # 不报错

14.5 set() 去重可能打乱顺序

nums = [3, 1, 2, 3, 1]
result = list(set(nums))
print(result)

结果不一定是:

[3, 1, 2]

如果需要保留顺序,推荐:

nums = [3, 1, 2, 3, 1]
result = list(dict.fromkeys(nums))
print(result)

结果:

[3, 1, 2]

14.6 update() 添加字符串时会拆成字符

s = set()
s.update("python")
print(s)

可能输出:

{'p', 'y', 't', 'h', 'o', 'n'}

如果想把整个字符串作为一个元素,应该使用 add()

s = set()
s.add("python")
print(s)

结果:

{'python'}

14.7 集合中的 True 和 1 可能被认为相同

在 Python 中,True1 在某些比较中是相等的。

print(True == 1)

结果:

True

因此:

s = {True, 1, 2}
print(s)

结果可能是:

{True, 2}

或者:

{1, 2}

这说明集合会根据“是否相等”来判断是否重复。

14.8 集合元素必须可哈希

集合底层需要通过哈希值来快速查找元素,所以集合里的元素必须是可哈希对象。

简单理解:

不可变对象通常可以放入集合。
可变对象通常不能放入集合。

可以放:

s = {1, "abc", (1, 2)}

不能放:

s = {[1, 2]}      # 列表不能放
s = {{1, 2}}      # 集合不能放
s = {{"a": 1}}    # 字典不能放

15. 综合案例

15.1 统计两个班共同报名的学生

class_a = {"张三", "李四", "王五", "赵六"}
class_b = {"李四", "王五", "钱七", "孙八"}

common_students = class_a & class_b

print("两个班都报名的学生:", common_students)

结果:

两个班都报名的学生: {'李四', '王五'}

15.2 统计所有报名学生

class_a = {"张三", "李四", "王五", "赵六"}
class_b = {"李四", "王五", "钱七", "孙八"}

all_students = class_a | class_b

print("所有报名学生:", all_students)
print("总人数:", len(all_students))

结果:

所有报名学生: {'张三', '李四', '王五', '赵六', '钱七', '孙八'}
总人数: 6

15.3 找出只报名 Python 的学生

python_class = {"张三", "李四", "王五", "赵六"}
java_class = {"李四", "王五", "钱七", "孙八"}

only_python = python_class - java_class

print("只报名 Python 的学生:", only_python)

结果:

只报名 Python 的学生: {'张三', '赵六'}

15.4 统计文章中出现过的不同字符

text = "hello python"
chars = set(text)

print(chars)
print("不同字符数量:", len(chars))

可能输出:

{'h', 'e', 'l', 'o', ' ', 'p', 'y', 't', 'n'}
不同字符数量: 9

15.5 判断列表中是否有重复用户名

users = ["alice", "bob", "tom", "alice"]

if len(users) == len(set(users)):
    print("没有重复用户名")
else:
    print("存在重复用户名")

结果:

存在重复用户名

16. 课堂练习

练习 1:列表去重

有如下列表:

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

请使用集合去掉重复元素。

参考答案:

nums = [1, 2, 2, 3, 4, 4, 5]
result = set(nums)
print(result)

练习 2:找共同好友

有两个用户的好友列表:

user_a = {"小明", "小红", "小刚", "小美"}
user_b = {"小红", "小美", "小强", "小丽"}

请找出他们的共同好友。

参考答案:

user_a = {"小明", "小红", "小刚", "小美"}
user_b = {"小红", "小美", "小强", "小丽"}

common = user_a & user_b
print(common)

练习 3:找所有参加活动的人

activity_1 = {"张三", "李四", "王五"}
activity_2 = {"王五", "赵六", "钱七"}

请找出参加过任意一个活动的人。

参考答案:

activity_1 = {"张三", "李四", "王五"}
activity_2 = {"王五", "赵六", "钱七"}

all_people = activity_1 | activity_2
print(all_people)

练习 4:找只参加第一个活动的人

activity_1 = {"张三", "李四", "王五"}
activity_2 = {"王五", "赵六", "钱七"}

请找出只参加第一个活动,没有参加第二个活动的人。

参考答案:

activity_1 = {"张三", "李四", "王五"}
activity_2 = {"王五", "赵六", "钱七"}

only_first = activity_1 - activity_2
print(only_first)

练习 5:判断是否有重复数据

ids = [1001, 1002, 1003, 1002]

请判断这个列表中是否存在重复编号。

参考答案:

ids = [1001, 1002, 1003, 1002]

if len(ids) == len(set(ids)):
    print("没有重复编号")
else:
    print("存在重复编号")

17. 课堂总结

set 是 Python 中非常实用的一种数据类型。

可以用一句话总结:

set 是一种无序、不重复、可修改的数据集合。

最常见用途:

  1. 去重
  2. 判断元素是否存在
  3. 求交集
  4. 求并集
  5. 求差集
  6. 判断集合关系

最常用代码:

s = set()        # 创建空集合
s.add(x)         # 添加一个元素
s.update(data)   # 批量添加元素
s.remove(x)      # 删除元素,不存在会报错
s.discard(x)     # 删除元素,不存在不报错
x in s           # 判断元素是否存在
a & b            # 交集
a | b            # 并集
a - b            # 差集
a ^ b            # 对称差集

教学记忆口诀:

集合 set 三特点:
元素不重复,
数据无顺序,
查找速度快。

再补充一句:

如果关心顺序,用 list。
如果关心去重和查找,用 set。
0
博主关闭了当前页面的评论