Python集合(Set)
About
Python 2.3版本的時候,引入了一種新的資料型別——集合(set)。 集合是由序列(也可以是其他的可迭代物件)構建的,是無序的可變的資料型別。 Python中,集合用一對大括號“{}”表示,集合內的各元素用逗號分隔。
s = set()
print(s) # set([])
s1 = {1, 2, 3}
print(s1) # set([1, 2, 3])
注意集合字典的區別,字典雖然也是一對大括號,但是其內是鍵值對形式的,而集合是用逗號隔開元素的,大家建立集合時需注意。我們也可通過set()函式將列表、元組等其他的可迭代的物件轉換為集合。
s = set([1, 2, 1, 3, 4])
print(s) # 3. {1, 2, 3, 4}
可以看出,轉換的時候,集合會自動將元素去重,這也是集合的特性:集合內的每個元素都是唯一的,不可重複! 除此之外,集合的元素只能是不可變型別的資料型別,也就是可雜湊型別,而比如列表,字典,和集合本身則不可作為集合的元素。
集合的基本操作
首先,我們來說,集合不支援什麼操作:
集合不支援:索引、切片,複製,拼接
s = set(range(10))
print(s) # set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
# print(s[0]) # TypeError: 'set' object does not support indexing
# print(s[0:3]) # TypeError: 'set' object has no attribute '__getitem__'
s1 = {'a', 'b'}
# s2 = s + s1 # TypeError: unsupported operand type(s) for +: 'set' and 'set'
s2 = s1 * 3 # TypeError: unsupported operand type(s) for *: 'set' and 'int'
可以看到,集合確實不支援這些!
成員資格測試
但是支援什麼呀,沒錯,成員資格測試還是可以的。
s = set(range(10))
print(1 in s) # True
print(1 not in s) # False
for迴圈取值
s = set(range(10))
for item in s:
print(item)
'''
0
1
2
3
4
5
6
7
8
9
'''
留個思考題,集合能while迴圈列印其中的值嘛?為啥能for迴圈取值? 雖然基本操作不多,但是常用方法不少。我們一起來看看。
集合的常用方法
set.add(obj)
為集合新增元素:
s = set()
s.add(1)
s.add(2)
s.add(1)
print(s) # set([1, 2])
由於去重的特性,集合將重複新增的元素過濾掉了。
set1.update(set2)
s1 = {1, 2}
s2 = {1, 2, 3, 4}
s1.update(s2)
print(s1) # set([1, 2, 3, 4])
通過set.update()
將另一個集合(s2)更新到當前集合(s1)中。同樣的,會去重。
刪除之:set.pop(obj)
s = {1, 2, 3, 4}
print(s.pop()) # 1
print(set().pop()) # KeyError: 'pop from an empty set'
隨機刪除集合元素並將該元素返回,如果集合為空則丟擲KeyError
。
set.remove() & set.discard()
set.remove() & set.discard()
用來刪除集合中的指定元素:
s = {1, 2, 3, 4}
s.remove(1)
print(s) # {2, 3, 4}
s.remove('a') # KeyError: 'a'
s1 = {1, 2, 3, 4}
s1.discard(1)
print(s1) # {2, 3, 4}
print(s1.discard('a')) # None
print(s1) # {2, 3, 4}
注意,set.remove()和set.discard()都是用來刪除指定的元素,但區別是set.remove()如果指定的元素不存在則會報錯,而set.discard()不會報錯,而是返回None。
del 刪除集合
由於集合是無序的,所以,del只能刪除整個集合:
s = {1, 2, 3, 4}
# del s[0] # TypeError: 'set' object doesn't support item deletion
del s
print(s) # NameError: name 's' is not defined
第一個報錯是不能刪除集合中的元素。第二個是之前已經將變數s
刪除,最後引用的時候,發現沒有報的錯。
set.clear()
set.clear()
清空集合
s = {1, 2, 3, 4}
s.clear()
print(s) # set()
集合的元素巢狀
前文中說集合中的元素只能是不可變型別的資料,所以,集合中的元素就只能是數字、字串和元組。
s1 = {1, 2.3, 'abc', (1, 2, [3, 4])} # TypeError: unhashable type: 'list'
由第二行的報錯來看,元組要想稱為集合的元素或者字典的key時,其內的元素必須全部為不可變型別才行。
集合的運算
現在提個需求,現有個培訓學校歐德博愛開設了Python和Linux兩門課程,來學習的同學都有如下情況:
-
有的同學學習Linux
-
有的學習Python
-
還有的既學了Linux又學了Python
那現在需求來了,我們要對這些同學的情況做統計,比如找出兩門課都報了的同學? 那採用什麼資料型別呢?這裡先用列表舉例。
learn_python = ['小a', '小b', '小c', '小麻雀', '葫蘆娃']
learn_linux = ['小c', '小d', '小e', '小東北', '小麻雀']
learn_p_l = []
for p in learn_python:
if p in learn_linux:
learn_p_l.append(p)
print(learn_p_l) # ['小c', '小麻雀']
那要找出只學習了linux的同學呢?
learn_python = ['小a', '小b', '小c', '小麻雀', '葫蘆娃']
learn_linux = ['小c', '小d', '小e', '小東北', '小麻雀']
learn_l = []
for l in learn_linux:
if l not in learn_python:
learn_l.append(l)
print(learn_l) # ['小d', '小e', '小東北']
這麼做是不是特別麻煩?在Python中,懶惰既是美德!所以Python給我們提供了一種簡便的方法,使用集合來做這些事情。
交集
首先來記住三個方法一個運算子:
-
set1.intersection(set2, set3...),以新集合的形式返回兩個或多個集合的交集。
-
set1.intersection_update(set2),兩個集合求交集(大家都有的),然後將結果覆蓋(相當於先對set1做clear操作)到當前(原地操作,並沒有產生新的集合)集合中(set1)。
-
&
運算子,intersection()方法對應的運算子,其實就是簡寫形式。 -
set1.isdisjoint(set2),該方法用來判斷兩個集合是否有交集,如果有交集,返回False,沒有交集返回True。
示例:找出即學習了Python又學習了Linux的同學。
learn_python = {'小a', '小b', '小c', '小麻雀', '葫蘆娃'}
learn_linux = {'小c', '小d', '小e', '小東北', '小麻雀'}
new_set1 = learn_python.intersection(learn_linux)
print(new_set1) # {'小麻雀', '小c'}
new_set2 = learn_python & learn_linux
print(new_set2) # {'小麻雀', '小c'}
print(learn_python) # {'葫蘆娃', '小a', '小麻雀', '小b', '小c'}
intersection()
方法將結果以新的集合形式返回,並沒有改變原集合。
learn_python = {'小a', '小b', '小c', '小麻雀', '葫蘆娃'}
learn_linux = {'小c', '小d', '小e', '小東北', '小麻雀'}
learn_python.intersection_update(learn_linux)
print(learn_python) # {'小c', '小麻雀'}
可以發現,intersection_update()
方法求出結果後,並沒有賦值給新的集合,而是將結果(set1 & set2)覆蓋到(可以理解為先清空)set1集合中。
learn_python = {'小a', '小b', '小c', '小麻雀', '葫蘆娃'}
learn_linux = {'小c', '小d', '小e', '小東北', '小麻雀'}
learn_java = {'Neeo', 'Anthony'}
print(learn_python.isdisjoint(learn_linux)) # False
print(learn_python.isdisjoint(learn_java)) # True
可以發現,Python和Linux兩門學科是有同學同時學習的,所以返回了False。而沒有同學同時學了Python和學Java,所以返回了True。
並集
首先來記住求並集的方法和運算子:
-
set1.union(set2, set3...),該方法以新集合的形式返回兩個或多個集合的並集,既包含了兩個集合的所有元素,並且重複的元素只會出現一次(去重)。
-
|
,求並集的運算子為管道符|
。
示例:找出所有來歐德博愛學習課程的人。
learn_python = {'小a', '小b', '小c', '小麻雀', '葫蘆娃'}
learn_linux = {'小c', '小d', '小e', '小東北', '小麻雀'}
new_set1 = learn_python.union(learn_linux)
print(new_set1) # {'小東北', '小c', '小e', '葫蘆娃', '小d', '小a', '小麻雀', '小b'}
new_set2 = learn_python | learn_linux
print(new_set2) # {'小東北', '小e', '小d', '小c', '小a', '小b', '葫蘆娃', '小麻雀'}
差集 首先,來記住求差集的方法和運算子:
-
set1.difference(set2, set3...),返回移除兩個或多個集合中重複元素後的新集合,也就是隻保留我自己(獨有的)。
-
-
,求差集的運算子為-
。 -
set1.difference_update(set2, set3...),該方法直接在原集合(set1)中移除重複元素,是原地操作,並沒有返回值。
找出只學習了Python(或者Linux)課程的人。
learn_python = {'小a', '小b', '小c', '小麻雀', '葫蘆娃'}
learn_linux = {'小c', '小d', '小e', '小東北', '小麻雀'}
new_set1 = learn_python.difference(learn_linux)
print(new_set1) # {'小b', '葫蘆娃', '小a'}
new_set2 = learn_python - learn_linux
print(new_set2) # {'小b', '葫蘆娃', '小a'}
print(learn_python) # {'小b', '葫蘆娃', '小麻雀', '小c', '小a'}
可以看到,新的集合中,只保留了僅學Python學科的學生,並沒有改變原有集合。
learn_python = {'小a', '小b', '小c', '小麻雀', '葫蘆娃'}
learn_linux = {'小c', '小d', '小e', '小東北', '小麻雀'}
learn_python.difference_update(learn_linux)
print(learn_python) # {'葫蘆娃', '小a', '小b'}
當前集合(learn_python)移除與集合(learn_linux)重複的元素後,將結果覆蓋到當前集合中(learn_python)。
對稱差集
首先,來記住求差集的方法和運算子:
-
set1.symmetric_difference(set2),返回兩個或多個集合中不重複元素後的新集合。也就是你獨有的加上我獨有的,合一起後的結果。
-
^
,求對稱差集的運算子為^
。 -
set1.symmetric_difference_update(set2),該方法返回了兩個或多個集合中不重複元素,並將結果覆蓋到當前集合中(set1),是原地操作,沒有返回值。
示例:找出沒有同時學習Python和Linux的學生。
learn_python = {'小a', '小b', '小c', '小麻雀', '葫蘆娃'}
learn_linux = {'小c', '小d', '小e', '小東北', '小麻雀'}
new_set1 = learn_python.symmetric_difference(learn_linux)
print(new_set1) # {'葫蘆娃', '小a', '小e', '小d', '小東北', '小b'}
new_set2 = learn_python ^ learn_linux
print(new_set2) # {'小d', '葫蘆娃', '小a', '小b', '小東北', '小e'}
print(learn_python) # {'葫蘆娃', '小麻雀', '小b', '小c', '小a'}
有前兩個列印結果可以看到,移除兩個集合的交集部分就是對稱差集的結果。
learn_python = {'小a', '小b', '小c', '小麻雀', '葫蘆娃'}
learn_linux = {'小c', '小d', '小e', '小東北', '小麻雀'}
learn_python.symmetric_difference_update(learn_linux)
print(learn_python) # {'小e', '葫蘆娃', '小b', '小東北', '小d', '小a'}
通過列印可以看到,在求完對稱差集後,將結果覆蓋到當前的集合中(learn_python)。
子集 & 超集
首先,來記住兩個方法:
-
set1.issubset(set2),該方法用來判斷一個集合是否為另一個集合的子集。
-
set1.issuperset(set2),該方法用來判斷一個集合是否為另一個集合的超集。
s1 = {1, 2}
s2 = {1, 2, 3, 4}
print(s1.issubset(s2)) # True
print(s1.issuperset(s2)) # False
上例,集合s1是另一個集合s2的子集,或者說集合s2是集合s1的超集。 還有一種情況需要注意:
s1 = {1, 2}
s2 = {1, 2}
print(s1.issubset(s2)) # True
print(s1.issuperset(s2)) # True
上例中,當兩個集合元素一致時,兩個集合的關係可以稱為互為子集。
集合中的關係運算
在集合中,我們可以使用關係運算符來判斷兩個集合的包含關係。
s1 = {1, 2}
s2 = {1, 2, 3, 4}
print(s1 < s2) # True
print(s1 <= s2) # True
print(s1 > s2) # False
print(s1 >= s2) # False
print(s1 == s2) # False
print(s1 != s2) # True
在集合中,關係運算符符包括:
-
小於(<),一個集合是否小於另一個集合。
-
小於等於(<=),一個集合是否小於等於另一個集合。
-
大於(>),一個集合是否大於另一個集合。
-
大於等於(>=),一個集合是否大於等於另一個集合。
-
等於(==),一個集合是否等於另一個集合。
-
不等於(!=),一個集合是否不等於另一個集合。
再次強調,在集合中,關係運算符只是表示兩個集合的包含關係,而不是表示兩個集合的大小關係。
方法 | 描述 | 重要程度 |
---|---|---|
set.add(obj) | 新增元素 | * |
set1.issuperset(set2) | 判斷當前集合是否為另一個集合的超集 | *** |
set1.issubset(set2) | 判斷當前集合是否為另一個集合的子集 | *** |
set1.sysmmetric_difference_update(set2) | 求對稱差集並將結果更新到當前集合內 | *** |
set1.sysmmetric_difference(set2) | 對稱差集:^ | * |
set1.update(set2) | 另一個集合更新當前集合 | * |
set.copy() | 淺拷貝 | **** |
set.discard(obj) | 刪除指定元素 | * |
set.pop() | 隨機刪除集合內元素並將此元素返回 | * |
set.clear() | 清空集合 | *** |
set.remove(obj) | 刪除指定元素 | **** |
set1.difference_update(set2) | 求差集並將結果更新到當前集合內 | **** |
set1.difference(set2) | 差集: - | * |
set1.union(set2) | 並集: | | * |
set1. isdisjoint(set2) | 兩個集合沒有交集返回True,否則返回False | *** |
set1.instersection_update(set2) | 求交集並將結果更新到當前集合內 | **** |
set1.instersection(set2) | 交集: & | * |
除此之外,還有以下內建函式可以應用於集合。
內建函式 | 描述 | 重要程度 |
---|---|---|
len(set) | 返回集合的元素個數 | * |
max(set) | 返回集合內的最大元素 | **** |
min(set) | 返回集合內的最小元素 | **** |
sum(set) | 返回集合元素之和 | *** |
sorted(set) | 排序集合內的元素 | * |
同樣的,這些內建函式在應用於集合時,要注意集合內的元素型別,比如求最大或最小時,其中的元素必須是同類型,不然怎麼比較呀!
總結 讓我們來總結下:
-
按照可變與不可變來說:
-
可變型別:列表、字典、集合。
-
不可變型別:數字、字串、元組。
-
-
按照存放值的數量來說:
-
一個值:數字,字串。
-
多個值:列表、元組、字典、集合。 注意,一串字串在客觀上也可以理解為一個元素,比如在列表中,字串再長,也只是列表的一個元素。數字也一樣。
-
-
按照取值方式:
-
直接取值:數字。
-
序列型別:字串、元組、列表。
-
對映型別:字典。
-
Reference:
圖片來源:RealPython