從創(chuàng)建到去重詳解Python中集合操作的完全指南
摘要
集合(Set)是 Python 中一種非常實用的數(shù)據(jù)結(jié)構(gòu),核心特點是無序且唯一。它最大的用途就是去重,另外還有高效的成員檢測和豐富的集合運算(并集、交集、差集等)。本文我們將從零開始,系統(tǒng)學(xué)習(xí)集合的創(chuàng)建、基本操作、運算方法、去重應(yīng)用,以及不可變的 frozenset。通過大量代碼示例和圖表,幫助我們快速掌握集合的各種用法
1. 前置知識點
在開始學(xué)習(xí)集合之前,我們需要先了解幾個基礎(chǔ)概念
可迭代對象:可以逐個訪問元素的對象,如列表、元組、字符串等。
可變 vs 不可變:
- 可變:創(chuàng)建后可修改(列表、字典、集合)
- 不可變:創(chuàng)建后不能修改(數(shù)字、字符串、元組)
哈希(Hash):快速查找技術(shù)。不可變對象有固定哈希值,可變對象不能哈希。
2. 什么是集合(Set)
2.1 集合的核心特點
集合這貨啊,簡單來說就是"無序且不重復(fù)"的元素容器 。它有四個核心特點,我們來一個個看:
1. 無序性
集合里的元素沒有固定順序,不支持下標(biāo)訪問。也就是說,我們不能用 set[0] 這種方式來取元素。那怎么訪問呢?只能通過遍歷或者直接判斷元素是否存在。好處是什么呢?就是我們不需要關(guān)心元素的排列順序,只要關(guān)心"這個元素在不在集合里"就行。
s = {'a', 'b', 'c', 'd', 'e', 'f'}
print(s) # 每次輸出順序可能不同,例如: {'c', 'a', 'f', 'd', 'b', 'e'}
2. 唯一性
這是集合最迷人的特點——自動去重!當(dāng)我們往集合里添加重復(fù)元素時,Python 會自動忽略它。這個特性在實際開發(fā)中超級實用,比如我們想找出列表中不重復(fù)的元素,直接轉(zhuǎn)成集合就搞定了。
s = {1, 2, 3, 2, 1, 4, 3}
print(s) # 輸出: {1, 2, 3, 4} - 自動去重了!
3. 可變性
集合本身是可以修改的,我們可以隨時添加或刪除元素。不過要注意,集合里的元素必須是不可變類型,比如字符串、數(shù)字、元組等。列表和字典這種可變類型是不能作為集合元素的,因為我們沒辦法確定它的哈希值。
那什么是不可變類型呢?簡單來說,就是"創(chuàng)建后不能改變"的數(shù)據(jù)類型。在 Python 中:
- 可哈希(hashable)的類型:int、float、str、bool、None、tuple(且元組內(nèi)全是不可變元素)、frozenset
- 不可哈希(unhashable)的類型:list、dict、set
為什么集合元素必須可哈希呢?因為集合底層用到了哈希表技術(shù),它需要通過元素的哈希值來快速定位元素。如果元素是可以改變的,那它的哈希值就可能變化,集合就亂了套了。所以 Python 直接規(guī)定:集合元素必須是不可變類型!
# 這些可以:
s1 = {1, 2, 3} # 整數(shù)
s2 = {'a', 'b', 'c'} # 字符串
s3 = {(1, 2), (3, 4)} # 元組(元素都是不可變的)
# 這些會報錯:
s4 = {[1, 2], [3, 4]} # ? TypeError: unhashable type: 'list'
s5 = {{'a': 1}} # ? TypeError: unhashable type: 'dict'
s6 = {{1, 2}} # ? TypeError: unhashable type: 'set'
4. 高效操作
集合底層基于哈希表實現(xiàn),這意味著查找、添加、刪除元素的時間復(fù)雜度都是 O(1),也就是常數(shù)時間。無論集合里有100個元素還是100萬個元素,操作速度都差不多。這可比列表的線性查找快多了!
2.2 集合 vs 列表 vs 元組對比
光說集合可能不太好理解,我們來把它和列表、元組放一起對比看看 ??:
| 特性 | 集合(set) | 列表(list) | 元組(tuple) |
|---|---|---|---|
| 有序性 | ? 無序 | ? 有序 | ? 有序 |
| 唯一性 | ? 自動去重 | ? 可以重復(fù) | ? 可以重復(fù) |
| 可變性 | ? 可變 | ? 可變 | ? 不可變 |
| 索引訪問 | ? 不支持 | ? 支持 | ? 支持 |
| 元素類型 | 必須是不可變類型 | 可以是任意類型 | 可以是任意類型 |
| 查找效率 | O(1) 極快 | O(n) 較慢 | O(n) 較慢 |
| 內(nèi)存占用 | 較高 | 較低 | 最低 |
從表格能看出來,集合最適合的場景就是:需要快速查找、需要去重、需要集合運算的時候 。如果你需要保持元素順序或者通過索引訪問,那就用列表;如果你需要存儲固定數(shù)據(jù)、追求極致性能,那就用元組。
3. 集合創(chuàng)建(set creation)
3.1 使用花括號創(chuàng)建
這是最直接的方式,直接用花括號把元素括起來,元素之間用逗號分隔:
# 基本創(chuàng)建
fruits = {"apple", "banana", "orange"}
print(fruits) # {'banana', 'orange', 'apple'}
# 數(shù)字集合
numbers = {1, 2, 3, 4, 5}
print(numbers) # {1, 2, 3, 4, 5}
# 混合類型(只要是不可變類型就行)
mixed = {"hello", 123, (1, 2, 3)}
print(mixed) # {'hello', 123, (1, 2, 3)}
還有一點很方便——自動去重!如果我們寫重復(fù)的元素,Python 會自動幫我們?nèi)サ簦?/p>
s = {1, 2, 3, 2, 1, 4, 3}
print(s) # {1, 2, 3, 4} - 自動去重了!
不過這里有個坑:空的花括號 {} 創(chuàng)建的不是空集合,而是空字典! 要創(chuàng)建空集合必須用 set()
# ? 這是字典,不是集合!
empty_dict = {}
print(type(empty_dict)) # <class 'dict'>
# ? 這才是空集合
empty_set = set()
print(type(empty_set)) # <class 'set'>
print(empty_set) # set()
3.2 使用 set() 函數(shù)創(chuàng)建
set() 函數(shù)可以創(chuàng)建集合,還有幾個常用用法:
1. 創(chuàng)建空集合
empty_set = set() print(empty_set) # set()
2. 從列表、元組、字符串轉(zhuǎn)換
這個功能超級實用!我們可以把其他可迭代對象轉(zhuǎn)成集合,自動去重:
# 從列表創(chuàng)建(自動去重)
lst = [1, 2, 3, 2, 1, 4]
s1 = set(lst)
print(s1) # {1, 2, 3, 4}
# 從元組創(chuàng)建
tup = (1, 2, 3, 2, 1)
s2 = set(tup)
print(s2) # {1, 2, 3}
# 從字符串創(chuàng)建(把字符拆成集合,自動去重)
text = "hello"
s3 = set(text)
print(s3) # {'h', 'e', 'l', 'o'} - 注意 'l' 只出現(xiàn)一次
3. 從集合創(chuàng)建(復(fù)制)
original = {1, 2, 3}
copied = set(original)
print(copied) # {1, 2, 3}
3.3 集合推導(dǎo)式創(chuàng)建
和列表推導(dǎo)式類似,集合也有推導(dǎo)式!語法幾乎一樣,只是把方括號換成花括號。
# 基本語法
squares = {x**2 for x in range(1, 6)}
print(squares) # {16, 1, 4, 9, 25}
# 帶條件篩選
even_squares = {x**2 for x in range(1, 10) if x % 2 == 0}
print(even_squares) # {16, 4, 36, 64}
# 從字符串過濾
text = "hello world"
unique_vowels = {char for char in text if char in 'aeiou'}
print(unique_vowels) # {'e', 'o'}
# 復(fù)雜一點的條件
pairs = {(x, y) for x in range(3) for y in range(3)}
print(pairs) # {(0, 0), (0, 1), (0, 2), (1, 0), ...}
集合推導(dǎo)式和列表推導(dǎo)式的區(qū)別:
- 列表推導(dǎo)式用方括號
[] - 集合推導(dǎo)式用花括號
{} - 集合會自動去重,列表不會
3.4 集合創(chuàng)建方式對比
我們來對比一下這三種創(chuàng)建方式:
| 創(chuàng)建方式 | 適用場景 | 示例 |
|---|---|---|
花括號 {} | 已知具體元素,直觀簡潔 | {1, 2, 3} |
set() 函數(shù) | 從其他數(shù)據(jù)轉(zhuǎn)換、創(chuàng)建空集合 | set([1,2,3])、set() |
| 集合推導(dǎo)式 | 批量生成、有篩選邏輯 | {x**2 for x in range(5)} |
使用建議:
- 如果知道具體元素,用花括號最直觀
- 如果要從列表去重,用
set() - 如果要批量生成有規(guī)律的元素,用推導(dǎo)式
4. 集合基本操作
4.1 訪問集合中的元素
我們前面說過,集合是無序的,所以不能用索引來訪問元素。那怎么訪問呢?兩個辦法:遍歷和成員檢測
1. 遍歷集合
fruits = {"apple", "banana", "orange"}
# 用 for 循環(huán)遍歷
for fruit in fruits:
print(fruit) # 每次順序可能不同
2. 成員檢測
想知道某個元素在不在集合里?用 in 操作符,超快!
fruits = {"apple", "banana", "orange"}
print("apple" in fruits) # True
print("grape" in fruits) # False
這就是集合最強(qiáng)大的地方——成員檢測超級快!無論集合里有10個還是100萬個元素,in 操作都是 O(1) 復(fù)雜度。
4.2 添加元素
往集合里添加元素有兩種方式:
1. add() - 添加單個元素
s = {1, 2, 3}
s.add(4)
print(s) # {1, 2, 3, 4}
# 如果添加已存在的元素,什么都不會發(fā)生(自動去重)
s.add(2)
print(s) # {1, 2, 3, 4} - 2 已經(jīng)在集合里了
2. update() - 添加多個元素
s = {1, 2, 3}
s.update([4, 5, 6]) # 從列表添加
print(s) # {1, 2, 3, 4, 5, 6}
s.update((7, 8)) # 從元組添加
print(s) # {1, 2, 3, 4, 5, 6, 7, 8}
s.update({9, 10}) # 從集合添加
print(s) # {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
s.update("hello") # 從字符串添加(每個字符)
print(s) # {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 'h', 'e', 'l', 'o'}
4.3 刪除元素
刪除元素有四種方式,各有特點:
1. remove() - 刪除指定元素(元素不存在會報錯)
s = {1, 2, 3, 4, 5}
s.remove(3)
print(s) # {1, 2, 4, 5}
# 如果元素不存在,會拋出 KeyError
s.remove(100) # KeyError: 100
2. discard() - 刪除指定元素(元素不存在不會報錯)
s = {1, 2, 3, 4, 5}
s.discard(3)
print(s) # {1, 2, 4, 5}
# 元素不存在也不會報錯,安全感滿滿!
s.discard(100) # 什么都不發(fā)生,程序繼續(xù)運行
3. pop() - 隨機(jī)刪除并返回一個元素
s = {1, 2, 3, 4, 5}
removed = s.pop()
print(f"刪除的元素: {removed}") # 隨機(jī)一個元素
print(f"剩余集合: {s}")
# 空集合調(diào)用 pop() 會報錯
empty_set = set()
empty_set.pop() # KeyError: 'pop from an empty set'
4. clear() - 清空集合
s = {1, 2, 3, 4, 5}
s.clear()
print(s) # set() - 變成空集合了
4.4 計算集合長度
想知道集合里有多少個元素?用 len() 函數(shù):
s = {1, 2, 3, 4, 5}
print(len(s)) # 5
# 自動去重,所以長度會變
s = {1, 1, 1, 2, 2, 3}
print(len(s)) # 3
我們來看個對比表格:
| 方法 | 作用 | 特點 |
|---|---|---|
add(x) | 添加單個元素 | 元素已存在則忽略 |
update(iterable) | 添加多個元素 | 可接受列表、元組、集合、字符串 |
remove(x) | 刪除指定元素 | 元素不存在會報錯 |
discard(x) | 刪除指定元素 | 元素不存在不報錯 |
pop() | 隨機(jī)刪除一個 | 返回被刪除的元素,空集合報錯 |
clear() | 清空集合 | 集合變?yōu)榭占?/td> |
5. 集合運算
5.1 并集(Union)
并集就是把兩個集合的所有元素合并在一起,重復(fù)的只保留一個。想象一下兩個班的學(xué)生,合并成一個班
運算符方式
set1 = {1, 2, 3}
set2 = {3, 4, 5}
# 用 | 運算符
result = set1 | set2
print(result) # {1, 2, 3, 4, 5}
方法方式
set1 = {1, 2, 3}
set2 = {3, 4, 5}
# 用 union() 方法
result = set1.union(set2)
print(result) # {1, 2, 3, 4, 5}
# union() 可以接收多個參數(shù)
result = set1.union(set2, {6, 7})
print(result) # {1, 2, 3, 4, 5, 6, 7}
我們來看個圖示:

5.2 交集(Intersection)
交集就是兩個集合中都有的元素。想象一下兩個班都參加了某項活動的學(xué)生
運算符方式
set1 = {1, 2, 3}
set2 = {3, 4, 5}
# 用 & 運算符
result = set1 & set2
print(result) # {3}
方法方式
set1 = {1, 2, 3}
set2 = {3, 4, 5}
# 用 intersection() 方法
result = set1.intersection(set2)
print(result) # {3}
# intersection() 可以接收多個參數(shù)
set3 = {3, 5, 6}
result = set1.intersection(set2, set3)
print(result) # {3}
圖示:

5.3 差集(Difference)
差集就是 A 集合中有但 B 集合中沒有的元素。想象一下參加了A活動但沒參加B活動的學(xué)生
運算符方式
set1 = {1, 2, 3}
set2 = {3, 4, 5}
# 用 - 運算符
result = set1 - set2
print(result) # {1, 2}
# 注意順序:set2 - set1 結(jié)果不同
result = set2 - set1
print(result) # {4, 5}
方法方式
set1 = {1, 2, 3}
set2 = {3, 4, 5}
# 用 difference() 方法
result = set1.difference(set2)
print(result) # {1, 2}
圖示:

5.4 對稱差集(Symmetric Difference)
對稱差集就是 A 和 B 的并集減去交集,也就是只屬于其中一個集合的元素。想象一下參加了A或B活動(但不同時參加兩個)的學(xué)生
運算符方式
set1 = {1, 2, 3}
set2 = {3, 4, 5}
# 用 ^ 運算符
result = set1 ^ set2
print(result) # {1, 2, 4, 5}
方法方式
set1 = {1, 2, 3}
set2 = {3, 4, 5}
# 用 symmetric_difference() 方法
result = set1.symmetric_difference(set2)
print(result) # {1, 2, 4, 5}
圖示 :

5.5 子集和超集
子集和超集用來判斷集合之間的關(guān)系:
- 子集:A 是 B 的子集,說明 A 的所有元素 B 都有
- 超集:A 是 B 的超集,說明 A 包含了 B 的所有元素
子集判斷
A = {1, 2, 3}
B = {1, 2, 3, 4, 5}
# 用 <= 判斷 A 是否是 B 的子集
print(A <= B) # True
print(A.issubset(B)) # True
# 用 < 判斷 A 是否是 B 的真子集(不是相等的情況)
print(A < B) # True
print(A == B) # False
超集判斷
A = {1, 2, 3, 4, 5}
B = {1, 2, 3}
# 用 >= 判斷 A 是否是 B 的超集
print(A >= B) # True
print(A.issuperset(B)) # True
# 用 > 判斷 A 是否是 B 的真超集
print(A > B) # True
print(A == B) # False
我們來看個總結(jié)表格:
| 運算 | 運算符 | 方法 | 說明 |
|---|---|---|---|
| 并集 | | | union() | 所有元素合并 |
| 交集 | & | intersection() | 共同元素 |
| 差集 | - | difference() | A有B沒有 |
| 對稱差集 | ^ | symmetric_difference() | 僅屬于其中一個 |
| 子集 | <= | issubset() | A全在B里 |
| 真子集 | < | - | A全在B里且不相等 |
| 超集 | >= | issuperset() | B全在A里 |
| 真超集 | > | - | B全在A里且不相等 |
6. 集合方法
我們在前面已經(jīng)學(xué)過增刪方法(add、remove、discard、pop、clear)和運算方法(union、intersection、difference)?,F(xiàn)在我們來聊聊那些原地修改的運算方法,也就是帶 _update 后綴的這些方法
6.1 增刪方法
這部分內(nèi)容我們在 第四章 已經(jīng)詳細(xì)介紹過了,這里簡單回顧一下:
| 方法 | 作用 |
|---|---|
add(x) | 添加單個元素 |
update(iterable) | 添加多個元素 |
remove(x) | 刪除指定元素,不存在會報錯 |
discard(x) | 刪除指定元素,不存在不報錯 |
pop() | 隨機(jī)刪除并返回元素 |
clear() | 清空集合 |
copy() | 復(fù)制集合 |
6.2 原地運算方法
這些方法會直接修改原集合,而不是返回一個新集合。它們的名字都有 _update 后綴
1. intersection_update() - 原地求交集
s1 = {1, 2, 3, 4}
s2 = {3, 4, 5, 6}
# 求交集,并原地修改 s1
s1.intersection_update(s2)
print(s1) # {3, 4}
2. difference_update() - 原地求差集
s1 = {1, 2, 3, 4}
s2 = {3, 4, 5, 6}
# 求差集,并原地修改 s1
s1.difference_update(s2)
print(s1) # {1, 2}
3. symmetric_difference_update() - 原地求對稱差集
s1 = {1, 2, 3, 4}
s2 = {3, 4, 5, 6}
# 求對稱差集,并原地修改 s1
s1.symmetric_difference_update(s2)
print(s1) # {1, 2, 5, 6}
4. update() - 原地求并集
s1 = {1, 2, 3}
s2 = {3, 4, 5}
# 求并集,并原地修改 s1
s1.update(s2)
print(s1) # {1, 2, 3, 4, 5}
我們來看個對比表格:
| 方法 | 作用 | 原集合變化 |
|---|---|---|
union() | 返回并集 | 不變 |
update() | 原地并集 | 修改原集合 |
intersection() | 返回交集 | 不變 |
intersection_update() | 原地交集 | 修改原集合 |
difference() | 返回差集 | 不變 |
difference_update() | 原地差集 | 修改原集合 |
symmetric_difference() | 返回對稱差集 | 不變 |
symmetric_difference_update() | 原地對稱差集 | 修改原集合 |
使用場景:如果我們不需要保留原集合,用原地運算方法可以節(jié)省內(nèi)存;如果需要保留原集合,用返回新集合的方法。
7. 集合去重應(yīng)用
集合最大的用處就是去重,我們來看看幾種常見場景
7.1 列表去重
lst = [1, 2, 3, 2, 1, 4, 3] unique = list(set(lst)) print(unique) # [1, 2, 3, 4] - 無序 # 如果需要保持順序,用 dict.fromkeys() # 原理:Python 3.7+ 字典保持插入順序,我們利用鍵的唯一性來去重 unique_ordered = list(dict.fromkeys(lst)) print(unique_ordered) # [1, 2, 3, 4]
7.2 字符串去重
text = "hello world"
unique_chars = set(text)
print(unique_chars) # {'h', 'e', 'l', 'o', ' ', 'w', 'r', 'd'}
# 按原順序 - 利用字典鍵的唯一性和保持順序的特性
unique_ordered = ''.join(dict.fromkeys(text))
print(unique_ordered) # helo wrd
7.3 復(fù)雜數(shù)據(jù)去重
對于列表里的復(fù)雜數(shù)據(jù)(字典、元組等),可以用 dict.fromkeys():
data = [{'a': 1}, {'b': 2}, {'a': 1}, {'b': 2}]
unique = list(dict.fromkeys(data))
print(unique) # [{'a': 1}, {'b': 2}]
8. frozenset:不可變集合
frozenset 就是"凍住"的集合——不可變的集合。它是 set 的兄弟,但一旦創(chuàng)建就不能修改
8.1 frozenset 的創(chuàng)建
# 從可迭代對象創(chuàng)建
fs1 = frozenset([1, 2, 3, 4, 5])
print(fs1) # frozenset({1, 2, 3, 4, 5})
# 從字符串創(chuàng)建
fs2 = frozenset("hello")
print(fs2) # frozenset({'h', 'e', 'l', 'o'})
# 從集合創(chuàng)建
fs3 = frozenset({1, 2, 3})
print(fs3) # frozenset({1, 2, 3})
8.2 frozenset 的使用場景
1. 作為字典的鍵
因為 frozenset 是不可變的,所以它可以哈希,能作為字典的鍵:
# 用 frozenset 作為字典的鍵
favorites = {
frozenset(['apple', 'banana']): '水果',
frozenset(['chicken', 'beef']): '肉類'
}
print(favorites) # {frozenset({'apple', 'banana'}): '水果', ...}
2. 作為集合的元素
普通的 set 是不能作為另一個 set 的元素的(因為 set 是可變的),但 frozenset 可以:
# 集合的集合 - 普通 set 不行
set_of_sets = {frozenset([1, 2]), frozenset([3, 4])}
print(set_of_sets) # {frozenset({1, 2}), frozenset({3, 4})}
3. 需要哈希的時候
frozenset 是可哈希的,普通 set 不行:
fs = frozenset([1, 2, 3])
print(hash(fs)) # 可以哈希
s = {1, 2, 3}
# print(hash(s)) # TypeError: unhashable type: 'set'
我們來看個對比表格:
| 特性 | set | frozenset |
|---|---|---|
| 可變性 | 可變 | 不可變 |
| 作為字典鍵 | ? 不行 | ? 可以 |
| 作為集合元素 | ? 不行 | ? 可以 |
| 哈希 | ? 不可哈希 | ? 可哈希 |
9. 總結(jié)
到這里,我們就把 Python 集合的內(nèi)容都學(xué)完了!我們來回顧一下
集合的核心特點:
- 無序:不能用索引訪問
- 唯一:自動去重
- 高效:查找、添加、刪除都是 O(1)
常用的操作:
- 創(chuàng)建:
{}、set()、集合推導(dǎo)式 - 增刪:add、remove、discard、pop、clear
- 運算:并集、交集、差集、對稱差集
- 判斷:子集、超集
特殊類型:frozenset:不可變集合,可以作為字典鍵或集合元素
以上就是從創(chuàng)建到去重詳解Python中集合操作的完全指南的詳細(xì)內(nèi)容,更多關(guān)于Python集合操作的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
python+pywinauto+lackey實現(xiàn)PC端exe自動化的示例代碼
這篇文章主要介紹了python+pywinauto+lackey實現(xiàn)PC端exe自動化的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
Python中用try-except-finally處理異常問題
這篇文章主要介紹了Python中用try-except-finally處理異常問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-12-12
pyqt5 QProgressBar清空進(jìn)度條的實例
今天小編就為大家分享一篇pyqt5 QProgressBar清空進(jìn)度條的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-06-06
基于python 將列表作為參數(shù)傳入函數(shù)時的測試與理解
這篇文章主要介紹了基于python 將列表作為參數(shù)傳入函數(shù)時的測試與理解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-06-06

