1. 创建
集合(set)用于保存一组不重复的元素。它适合表示标签、权限、已处理的任务和需要快速判断是否存在的数据。
使用花括号创建集合:
1topics = {"变量", "列表", "函数"}2numbers = {1, 2, 3, 3}3empty = set()45print(topics)6print(numbers) # {1, 2, 3}7print(empty) # set()
重复元素只会保留一份。空集合不能写成 {},因为 {} 表示空字典,创建空集合必须使用 set()。
也可以使用 set() 把列表、字符串等可遍历对象转换成集合:
1numbers = set([1, 2, 2, 3, 3])2letters = set("Python")34print(numbers) # {1, 2, 3}5print(sorted(letters)) # ['P', 'h', 'n', 'o', 't', 'y']
集合中的元素必须是可哈希的,字符串、数字和只包含可哈希元素的元组可以作为元素;列表和字典不能直接放进集合:
1valid = {"Python", 1, ("FastAPI", "LangChain")}23# invalid = {["Python", "FastAPI"]} # TypeError4print(valid)
集合主要用于成员判断和集合关系,不应该依赖它的遍历顺序,也不能像列表一样使用索引和切片。
2. 修改
add() 向集合中加入一个元素。如果元素已经存在,集合不会产生重复项:
1topics = {"变量", "列表"}23topics.add("函数")4topics.add("列表")56print(topics) # {'变量', '列表', '函数'},顺序仅用于示意
update() 可以一次加入多个元素,它会遍历参数并逐个添加:
1topics = {"变量"}2topics.update(["列表", "字符串"])34print(topics)
删除元素时,remove() 在元素不存在时会抛出 KeyError,discard() 则不会报错:
1topics = {"变量", "列表", "字符串"}23topics.remove("列表")4topics.discard("字典")56print(topics)
pop() 会删除并返回一个元素,但集合没有固定顺序,因此不能预测它具体删除哪一个元素:
1topics = {"变量", "列表", "字符串"}2removed = topics.pop()34print(removed)5print(topics)
需要删除全部元素时使用 clear():
1permissions = {"read", "write", "delete"}2permissions.clear()34print(permissions) # set()
3. 运算
集合运算可以直接表达两组数据之间的关系:
| 运算 | 符号 | 含义 |
|---|---|---|
| 并集 | | | 两组中出现过的所有元素 |
| 交集 | & | 同时出现在两组中的元素 |
| 差集 | - | 出现在左侧、但不在右侧的元素 |
| 对称差集 | ^ | 只出现在其中一组的元素 |
1python_topics = {"变量", "列表", "函数"}2ai_topics = {"函数", "Agent", "LangChain"}34print(python_topics | ai_topics) # 并集5print(python_topics & ai_topics) # 交集6print(python_topics - ai_topics) # 只属于 Python 集合7print(python_topics ^ ai_topics) # 只属于一侧
这些运算也有对应的方法,例如 union()、intersection()、difference() 和 symmetric_difference():
1frontend = {"HTML", "CSS", "JavaScript"}2backend = {"Python", "JavaScript"}34common = frontend.intersection(backend)5only_frontend = frontend.difference(backend)67print(sorted(common)) # ['JavaScript']8print(sorted(only_frontend)) # ['CSS', 'HTML']
集合运算会返回新的集合,不会修改参与运算的原集合。需要把结果写回原集合时,可以使用 |=、&= 或 -=:
1permissions = {"read", "write"}2permissions |= {"delete"}3permissions -= {"write"}45print(sorted(permissions)) # ['delete', 'read']
4. 判断
in 和 not in 是集合最常见的用法。集合专门为成员判断进行了优化,表达「是否已经存在」比遍历列表更直接:
1processed_ids = {101, 102, 103}23if 102 in processed_ids:4print("这个任务已经处理过")56if 200 not in processed_ids:7print("可以处理新任务")
可以使用 issubset() 判断一个集合是否是另一个集合的子集,使用 issuperset() 判断是否包含另一个集合:
1all_permissions = {"read", "write", "delete"}2user_permissions = {"read", "write"}34print(user_permissions.issubset(all_permissions)) # True5print(all_permissions.issuperset(user_permissions)) # True6print(user_permissions <= all_permissions) # True
isdisjoint() 用来判断两组数据是否没有共同元素:
1morning = {"早餐", "跑步"}2evening = {"晚餐", "阅读"}34print(morning.isdisjoint(evening)) # True
集合也可以直接放进 if 中。空集合是假值,非空集合是真值:
1pending = set()23if not pending:4print("没有待处理任务")
5. 遍历
可以使用 for 遍历集合,但不要依赖元素出现的顺序:
1tags = {"Python", "AI", "FastAPI"}23for tag in tags:4print(tag)
如果需要稳定的展示顺序,先使用 sorted() 得到一个列表:
1tags = {"Python", "AI", "FastAPI"}23for tag in sorted(tags):4print(tag)
集合不能通过索引读取元素。如果业务同时需要「不重复」和「固定顺序」,可以使用字典的键,或者在去重时保留列表顺序:
1tags = ["Python", "AI", "Python", "Web", "AI"]2unique_tags = list(dict.fromkeys(tags))34print(unique_tags) # ['Python', 'AI', 'Web']
这个写法利用了 Python 字典保留插入顺序的特性。如果只关心去重、不关心原顺序,直接使用 set(tags) 更简单。
6. 实例
权限判断是集合的常见场景。可以用交集判断用户是否拥有某项功能所需的全部权限:
1user_permissions = {"read", "write"}2required_permissions = {"read", "write"}34if required_permissions.issubset(user_permissions):5print("允许执行操作")6else:7print("权限不足")
标签去重也可以直接使用集合:
1raw_tags = ["python", "ai", "python", "web", "ai"]2tags = set(raw_tags)34print(tags)
如果需要统计两个用户共同关注的主题,可以使用交集;如果需要找出只属于某个用户的主题,可以使用差集:
1alice = {"Python", "AI", "摄影"}2bob = {"AI", "音乐", "摄影"}34print("共同主题:", alice & bob)5print("Alice 独有:", alice - bob)
如果集合本身也需要作为字典键,可以使用不可变集合 frozenset:
1permission_groups = {2frozenset({"read"}): "访客",3frozenset({"read", "write"}): "编辑者",4}56print(permission_groups[frozenset({"read"})]) # 访客
frozenset 支持成员判断和集合运算,但不能使用 add()、remove() 等方法修改。它适合表示需要固定下来、还要作为字典键或其他集合元素的权限组合。