查看: 89|回复: 1

Python集合运算深入:并集、交集、差集、对称差集用法与实战

[复制链接]
发表于 3 小时前 | 显示全部楼层 |阅读模式
Python 的集合(set)不仅支持去重,还内置了四种基本集合运算:并集、交集、差集和对称差集。这些运算对应数学中的集合论,但在代码中只需要一个运算符或方法即可完成,且性能极高(基于哈希表实现)。本文会用实际案例逐一讲解每种运算的运算符、方法、原地操作以及适用场景,最后给出一个好友推荐系统的实现示例。

一、并集(Union):合并所有元素并去重

并集返回两个(或多个)集合中的所有元素,自动去重。Python 提供两种方式:
- 运算符 `|`:两边都必须是集合。
- 方法 `union()`:可以接受任意可迭代对象(列表、元组、range、字典的键等)。

示例:
  1. a = {1, 2, 3, 4}
  2. b = {3, 4, 5, 6}
  3. print(a | b)  # {1, 2, 3, 4, 5, 6}
  4. print(a.union([4, 5]))  # {1, 2, 3, 4, 5},参数是列表
复制代码

当需要合并多个集合时,可以用链式 `|` 或 `union()` 传入多个参数:
  1. a = {1, 2}
  2. b = {2, 3}
  3. c = {3, 4}
  4. result = a | b | c  # {1, 2, 3, 4}
  5. result = a.union(b, c)  # 同上
复制代码

原地操作 `|=` 会直接修改原集合:
  1. a = {1, 2, 3}
  2. b = {3, 4, 5}
  3. a |= b  # a 变为 {1, 2, 3, 4, 5},原对象被修改
复制代码

实战:合并多个数据源的用户邮箱。
  1. email_sources = {
  2.     'web': {'alice@test.com', 'bob@test.com'},
  3.     'mobile': {'bob@test.com', 'david@test.com'},
  4.     'api': {'charlie@test.com', 'frank@test.com'},
  5. }
  6. all_emails = set().union(*email_sources.values())
  7. print(all_emails)  # {'alice', 'bob', 'charlie', 'david', 'frank'}
复制代码

二、交集(Intersection):找出共有元素

交集返回同时存在于所有集合中的元素。运算符 `&` 要求两边都是集合,方法 `intersection()` 可接受可迭代对象。
  1. a = {1, 2, 3, 4}
  2. b = {3, 4, 5, 6}
  3. print(a & b)  # {3, 4}
  4. print(a.intersection([3, 4, 5]))  # {3, 4}
复制代码

多个集合求交集:
  1. a = {1, 2, 3, 4, 5}
  2. b = {2, 3, 4, 5, 6}
  3. c = {3, 4, 5, 6, 7}
  4. print(a & b & c)  # {3, 4, 5}
  5. print(a.intersection(b, c))  # 同上
复制代码

原地操作 `&=` 会修改原集合。

实战:找同时满足多个条件的用户。
  1. purchased = {'alice', 'bob', 'charlie'}
  2. subscribed = {'alice', 'charlie', 'david'}
  3. reviewed = {'alice', 'david', 'frank'}
  4. loyal = purchased & subscribed & reviewed
  5. print(loyal)  # {'alice'}
复制代码

三、差集(Difference):获取独有的元素

差集返回前一个集合中去除与后一个集合共有元素后的剩余元素,**不对称**。运算符 `-` 要求两边都是集合,方法 `difference()` 可接受可迭代对象。
  1. a = {1, 2, 3, 4, 5}
  2. b = {4, 5, 6, 7, 8}
  3. print(a - b)  # {1, 2, 3}
  4. print(b - a)  # {6, 7, 8}
  5. print(a.difference([4, 5]))  # {1, 2, 3}
复制代码

多个集合求差集时,按顺序连续减去:
  1. a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
  2. b = {1, 2, 3}
  3. c = {4, 5}
  4. result = a - b - c  # 或 a.difference(b, c)
  5. print(result)  # {6, 7, 8, 9, 10}
复制代码

原地操作 `-=`。

实战:权限管理——排除被禁用的操作。
  1. all_perms = {'read', 'write', 'delete', 'admin', 'export', 'import'}
  2. role_editor = {'read', 'write', 'export'}
  3. forbidden = {'delete', 'admin', 'import'}
  4. actual = role_editor - forbidden
  5. print(actual)  # {'read', 'write', 'export'}
复制代码

四、对称差集(Symmetric Difference):找出仅属于一个集合的元素

对称差集返回只出现在其中一个集合中的元素,排除共有部分。运算符 `^` 要求两边都是集合,方法 `symmetric_difference()` 只接受一个参数(不能多个)。
  1. a = {1, 2, 3, 4}
  2. b = {3, 4, 5, 6}
  3. print(a ^ b)  # {1, 2, 5, 6}
  4. print(a.symmetric_difference(b))  # 同上
复制代码

验证对称等价式:`a ^ b == (a - b) | (b - a) == (a | b) - (a & b)`。

原地操作 `^=`。

实战:比较两个版本的功能变更。
  1. v1 = {'login', 'register', 'profile', 'search', 'cart'}
  2. v2 = {'login', 'register', 'profile', 'search', 'cart', 'chat', 'analytics'}
  3. changes = v1 ^ v2
  4. print(changes)  # {'chat', 'analytics'}
  5. added = v2 - v1
  6. removed = v1 - v2
复制代码

五、运算符 vs 方法:如何选择

- 运算符(| & - ^):代码简洁,但操作数都必须是集合。
- 方法(union, intersection, difference, symmetric_difference):可以接受列表、元组等可迭代对象,而且 `union` 和 `intersection` 支持多参数。
- 原地运算符(|= &= -= ^=)修改原集合,建议仅在需要性能优化(如大数据量循环)时使用,通常应优先创建新对象以避免副作用。

六、性能对比:集合 vs 列表

集合运算基于哈希表,时间复杂度远低于列表的线性搜索。例如,对 10000 个元素求交集,集合比列表快 100 倍以上。实际测试中,集合运算瞬间完成,而列表推导式则需要数百倍时间。因此,在需要频繁判断成员关系的场景中,应优先使用集合。

七、综合实战:好友推荐系统

假设用户数据如下,推荐规则:如果 A 和 B 不是好友,但有共同好友,则推荐 B 给 A。
  1. class FriendRecommender:
  2.     def __init__(self, friendships):
  3.         # friendships: dict of username -> set of friends
  4.         self.friends = friendships
  5.     def recommend(self, user):
  6.         if user not in self.friends:
  7.             return set()
  8.         user_friends = self.friends[user]
  9.         candidates = set()
  10.         for friend in user_friends:
  11.             # 每个好友的好友(排除自己和已好友)
  12.             candidates |= self.friends[friend] - user_friends - {user}
  13.         # 按共同好友数排序
  14.         from collections import Counter
  15.         common_counts = Counter()
  16.         for candidate in candidates:
  17.             common = user_friends & self.friends[candidate]
  18.             common_counts[candidate] = len(common)
  19.         # 返回按共同好友数降序排列的推荐列表
  20.         return common_counts.most_common()
  21. # 示例数据
  22. friendships = {
  23.     'Alice': {'Bob', 'Charlie', 'David'},
  24.     'Bob': {'Alice', 'Charlie', 'Eve'},
  25.     'Charlie': {'Alice', 'Bob', 'Frank'},
  26.     'David': {'Alice'},
  27.     'Eve': {'Bob'},
  28.     'Frank': {'Charlie'},
  29. }
  30. recommender = FriendRecommender(friendships)
  31. print(recommender.recommend('Alice'))
复制代码

此实现利用并集、差集、交集高效找出候选人和计算共同好友数。

八、注意事项

- 运算符两边必须都是集合,否则会抛出 TypeError;方法则可以接受任何可迭代对象。
- 运算符优先级:`-` 优先级高于 `|` 和 `&`,但低于比较运算符,必要时加括号。
- 对称差集方法只能传一个参数;多个集合的对称差需用链式 `^`。
- 集合元素必须是不可变类型(如数字、字符串、元组),不能包含列表或集合,否则无法参与运算。

掌握集合的四类运算,可以大幅简化数据处理代码,尤其适用于去重、筛选、差异对比等场景。建议优先使用方法以避免类型转换,在性能敏感处使用原地操作。
回复

使用道具 举报

发表于 3 小时前 | 显示全部楼层

Re: Python集合运算深入:并集、交集、差集、对称差集用法与实战

感谢分享!写得非常清晰,尤其是权限管理和版本对比的例子很有启发性。我自己之前一直只习惯用 `|` 和 `&`,看到 `difference()` 可以接受可迭代对象这点确实方便很多。想问一下,如果集合里的元素是自定义对象(比如需要重写 `__hash__` 和 `__eq__`),这些运算还能正常用吗?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-6-15 14:14 , Processed in 0.023237 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部