一、前言
集合(set)是 Python 中用于去重 和高效成员判断 的核心数据结构。
但很多初学者在创建集合时就踩了坑:
- 为什么
{}不是空集合? - 为什么列表不能放进集合?
set([1,2,3])和{1,2,3}有什么区别?- 如何从字符串、生成器创建集合?
本文将带你: ✅ 掌握 5 种集合创建方式 及其适用场景
✅ 避开 "{} 是字典" 的经典陷阱
✅ 理解 可哈希性 对集合元素的限制
✅ 学会用 集合推导式 高效构建
✅ 写出安全、高效、无 bug 的集合初始化代码
二、方式一:字面量语法(最常用)
使用花括号 {} 直接定义集合(元素用逗号分隔):
python
s1 = {1, 2, 3}
s2 = {"apple", "banana", "cherry"}
s3 = {1, "hello", 3.14} # 元素类型可混合(只要可哈希)
✅ 优点:
- 语法简洁
- 执行速度快(无需函数调用)
- 自动去重
💡 示例:
pythons = {1, 2, 2, 3} # → {1, 2, 3}
⚠️ 重大限制 :元素必须是可哈希的(如 int、str、tuple),不能是 list、dict、set。
三、方式二:使用 set() 构造器(最灵活)
通过内置函数 set() 从任意可迭代对象创建集合:
python
# 从列表(自动去重)
s1 = set([1, 2, 2, 3]) # {1, 2, 3}
# 从字符串(拆分为字符并去重)
s2 = set("hello") # {'h', 'e', 'l', 'o'}
# 从元组
s3 = set((1, 2, 3)) # {1, 2, 3}
# 从生成器
gen = (x for x in range(5))
s4 = set(gen) # {0, 1, 2, 3, 4}
# 创建空集合(唯一正确方式!)
empty = set() # ✅ 空集合
✅ 优势:
- 支持动态输入(变量、函数返回值等)
- 适用于任何可迭代对象
- 是创建空集合的唯一标准方式
⚠️ 经典陷阱:
pythons = {} # ❌ 这是空字典,不是集合! print(type(s)) # <class 'dict'>
四、方式三:集合推导式(高级技巧)
类似列表推导式,但结果是集合(自动去重):
python
# 提取偶数并去重
evens = {x for x in range(10) if x % 2 == 0}
# {0, 2, 4, 6, 8}
# 从字符串提取大写唯一字母
unique_upper = {c.upper() for c in "Hello World!" if c.isalpha()}
# {'H', 'E', 'L', 'O', 'W', 'R', 'D'}
# 平方去重
squares = {x**2 for x in [1, -1, 2, -2, 3]}
# {1, 4, 9}
✅ 优势:
- 一行完成过滤 + 转换 + 去重
- 性能优于先建列表再转集合
五、方式四:从其他集合或 frozenset 创建
python
original = {1, 2, 3}
copy_set = set(original) # 浅拷贝
frozen = frozenset(original)
from_frozen = set(frozen) # 转回可变集合
✅ 适用场景:集合复制、类型转换
六、方式五:使用 * 解包(Python 3.5+)
从现有可迭代对象解包创建新集合:
python
list1 = [1, 2, 3]
list2 = [3, 4, 5]
combined = {*list1, *list2} # {1, 2, 3, 4, 5}
✅ 优势:快速合并多个序列并去重
七、常见陷阱与避坑指南
❌ 陷阱 1:用 {} 创建空集合
python
s = {} # ❌ 字典
s = set() # ✅ 集合
📌 记忆口诀 :"空集合,用 set();非空集,用 {}。"
❌ 陷阱 2:放入不可哈希对象
python
# 以下全部报错!
s = {[1, 2]} # TypeError: unhashable type: 'list'
s = {{"a": 1}} # dict 不可哈希
s = {{1, 2}} # set 本身不可哈希
# ✅ 正确:改用元组
s = {(1, 2), (3, 4)}
✅ 原则 :集合中的每个元素必须是不可变且可哈希的。
❌ 陷阱 3:误以为字符串会被整体放入
python
s = set("abc") # {'a', 'b', 'c'} ← 拆成字符!
s = {"abc"} # {'abc'} ← 整个字符串作为一个元素
✅ 区别:
set("abc")→ 从可迭代对象构建(字符串可迭代){"abc"}→ 字面量,包含一个字符串元素
❌ 陷阱 4:在集合中放 float('nan')
python
s = {float('nan'), float('nan')}
print(len(s)) # 2!因为 NaN != NaN
✅ 注意:NaN 在集合中不会被去重(因其不等于自身)
八、性能对比:哪种创建方式最快?
测试创建包含 10 万个整数的集合:
| 方法 | 平均耗时(100 次) | 说明 |
|---|---|---|
{1, 2, ..., n} |
⚡ 最快(但仅适用于静态数据) | |
set(range(n)) |
✅ 快(推荐动态创建) | |
set([1,2,...,n]) |
🐢 较慢(先建列表再转集合,多一次内存分配) | |
| 集合推导式 | ✅ 快(接近 set(iter)) |
✅ 建议:
- 静态数据 → 用
{...} - 动态数据 → 用
set(iterable)或集合推导式 - 避免
set(list_literal)(先建列表浪费内存)
九、最佳实践总结
| 场景 | 推荐创建方式 |
|---|---|
| 非空静态集合 | {1, 2, 3} |
| 空集合 | set() |
| 从列表/字符串去重 | set(my_iterable) |
| 带条件过滤 | {x for x in iterable if condition} |
| 合并多个序列 | {*seq1, *seq2} |
| 确保元素可哈希 | 使用 int、str、tuple 等不可变类型 |
🌈 核心原则 :
"集合靠哈希实现,元素必须可哈希;空集用 set(),非空可用 {}。"
十、结语
感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!