1. 程式人生 > 其它 >Python集合(Set)

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