Set是什麼
大家好,恰逢初五迎財神,先預祝大家新年財源滾滾!!
在上一期詳解tuple元組的用法後,今天我們來看Python裡面最後一種常見的資料型別:集合(Set)
與dict類似,set也是一組key的集合,但不儲存value。由於key不能重複,所以,在set中,沒有重複的key。建立一個set,需要提供一個list作為輸入集集合,重複元素在set中會被自動被過濾,通過add(key)方法往set中新增元素,重複新增不會有效果。如果現在你發現我講的很模糊請不要著急。稍後會有海量例子為大家詳解。
總而言之,Set具有三個顯著特點:
- 無序
- 元素是獨一無二的,不允許出現重複的元素
- 可以修改集合本身,但集合中包含的元素必須是不可變型別
現在讓我們開啟Set奇幻之旅,我希望這篇文章是SegmentFault社群對於Set介紹最全的模範,哈哈!
定義一個Set
我們有兩種方式可以建立一個Set,可以使用內建的set()方法,或是使用中括號{}
建立模板如下:
x = set(<iter>)
x = {<obj>, <obj>, ..., <obj>}
現在讓我們來看例子~
set()內建方法建立
x = set(['foo', 'bar', 'baz', 'foo', 'qux']) # 傳入List
print(x)
y = set(('foo', 'bar', 'baz', 'foo', 'qux')) #傳入元組
print(y)
Out: {'qux', 'foo', 'bar', 'baz'} # 注意到無序了吧~
{'bar', 'qux', 'baz', 'foo'}
這裡要注意用set()內建方法建立時一定要傳遞一個可以迭代的引數,還有從輸出結果相信大家已經發現set的第一個特點了:無序
字串也是可迭代的,因此字串也可以傳遞給set()
s = 'quux'
a = set(s)
print(a)
Out: {'u', 'q', 'x'} # 無序,唯一
這裡又體現了set的第二個特點:元素唯一性
{} 方法建立
>>> x = {'foo', 'bar', 'baz', 'foo', 'qux'}
>>> x
{'qux', 'foo', 'bar', 'baz'}
這裡考慮到之後例子太多,實在不能每次都打print啦,這種形式大家看的更清楚,這個直接用{}建立很簡單,只要傳遞進元素就行啦
建立空集合
Set可以是空的。但是,請記住Python將空花括號{}解釋為空字典,因此定義空集的唯一方法是使用set()函式
>>> x = set()
>>> type(x)
<class 'set'>
>>> x = {}
>>> type(x)
<class 'dict'>
一個空集合用布林型別顯示為False
>>> x = set()
>>> bool(x)
False
>>> x or 1
1
>>> x and 1
set()
對比小結
對於這兩種方法建立Set,本質區別在於以下兩點
- set()的引數是可迭代的。它會生成要放入集合中的所有元素組成的List。
- 花括號 {} 中的物件完整地放入集合中,即使它們是可迭代的。
補充說明
集合中的元素可以是不同型別的物件,不一定非要是同一型別的,可以包含不同型別,比如:
>>> x = {42, 'foo', 3.14159, None}
>>> x
{None, 'foo', 42, 3.14159}
但同時不要忘記set元素必須是不可變的。例如,元組可以包括在集合中:
>>> x = {42, 'foo', (1, 2, 3), 3.14159}
>>> x
{42, 'foo', 3.14159, (1, 2, 3)}
但列表和字典是可變的,因此它們不能成為Set的元素:
>>> a = [1, 2, 3]
>>> {a}
Traceback (most recent call last):
File "<pyshell#70>", line 1, in <module>
{a}
TypeError: unhashable type: 'list'
>>> d = {'a': 1, 'b': 2}
>>> {d}
Traceback (most recent call last):
File "<pyshell#72>", line 1, in <module>
{d}
TypeError: unhashable type: 'dict'
Set大小以及成員
len()函式返回集合中元素的數量,而in和not in運算子可用於測試是否為Set中的元素:
>>> x = {'foo', 'bar', 'baz'}
>>> len(x)
3
>>> 'bar' in x
True
>>> 'qux' in x
False
Set基本操作
方法和運算子
許多可用於Python其他資料型別的操作對集合沒有意義。例如,無法對集合建立索引或切片。但是,Python在set物件上提供了運算子,這些操作符其實很多和數學裡是一模一樣的,相信數學好的朋友們對這部分簡直不要太熟悉
所以對於Set的操作除了用普通的內建方法,我們也可以使用運算子,比較方便
Union 並集
- 用法:計算兩個或更多集合的並集。
- 方法: x1.union(x2[, x3 ...])
- 運算子:x1 | x2 [| x3 ...]
讓我們新建兩個Set做測試:
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'baz', 'qux', 'quux'}
現在我們想求x1,x2的並集,如下圖所示:
具體實現方法如下,或是用方法,或是用操作符:
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'baz', 'qux', 'quux'}
>>> x1.union(x2)
{'foo', 'qux', 'quux', 'baz', 'bar'}
>>> x1 | x2
{'foo', 'qux', 'quux', 'baz', 'bar'}
如果有兩個以上的Set也是沒有問題的,原理都是一樣的:
>>> a = {1, 2, 3, 4}
>>> b = {2, 3, 4, 5}
>>> c = {3, 4, 5, 6}
>>> d = {4, 5, 6, 7}
>>> a.union(b, c, d)
{1, 2, 3, 4, 5, 6, 7}
>>> a | b | c | d
{1, 2, 3, 4, 5, 6, 7}
Intersection 交集
- 方法: x1.intersection(x2[, x3 ...])
- 運算子:x1 & x2 [& x3 ...]
- 用法:計算兩個或更多集合的交集。
現在還讓我們用剛才建立好的兩個set,所求部分如下圖:
實現仍然是兩種方法:
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'baz', 'qux', 'quux'}
>>> x1.intersection(x2)
{'baz'}
>>> x1 & x2
{'baz'}
多個集合的情況公示和方法依然有效,結果僅包含所有指定集合中都存在的元素。
>>> a = {1, 2, 3, 4}
>>> b = {2, 3, 4, 5}
>>> c = {3, 4, 5, 6}
>>> d = {4, 5, 6, 7}
>>> a.intersection(b, c, d)
{4}
>>> a & b & c & d
{4}
Difference 差集
- 方法: x1.difference(x2[, x3 ...])
- 運算子:x1 - x2 [- x3 ...]
- 用法:計算兩個或更多集合的差集。大白話說就是x1去除x1和x2的共有元素
下圖所示為x1.difference(x2)的目標結果:
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'baz', 'qux', 'quux'}
>>> x1.difference(x2)
{'foo', 'bar'}
>>> x1 - x2
{'foo', 'bar'}
還是老樣子,適用於2個及以上的集合:
>>> a = {1, 2, 3, 30, 300}
>>> b = {10, 20, 30, 40}
>>> c = {100, 200, 300, 400}
>>> a.difference(b, c)
{1, 2, 3}
>>> a - b - c
{1, 2, 3}
指定多個集合時,操作從左到右執行。在上面的示例中,首先計算a - b,得到{1,2,3,300}。然後從該集合中減去c,留下{1,2,3},具體流程如下圖所示:
Symmetric Difference 對稱差集
- 方法: x1.symmetric_difference(x2)
- 運算子:x1 ^ x2 1
- 用法:計算兩個或更多集合的差集。大白話說就是x1去除x1和x2的共有元素
下圖所示為x1.symmetric_difference(x2)的目標結果:
實現方法如下;
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'baz', 'qux', 'quux'}
>>> x1.symmetric_difference(x2)
{'foo', 'qux', 'quux', 'bar'}
>>> x1 ^ x2
{'foo', 'qux', 'quux', 'bar'}
老規矩,支援2個及以上set的連續操作:
>>> a = {1, 2, 3, 4, 5}
>>> b = {10, 2, 3, 4, 50}
>>> c = {1, 50, 100}
>>> a ^ b ^ c
{100, 5, 10}
當指定多個集合時,操作從左到右執行,奇怪的是,雖然 ^ 運算子允許多個集合,但.symmetric_difference()方法不允許
>>> a = {1, 2, 3, 4, 5}
>>> b = {10, 2, 3, 4, 50}
>>> c = {1, 50, 100}
>>> a.symmetric_difference(b, c)
Traceback (most recent call last):
File "<pyshell#11>", line 1, in <module>
a.symmetric_difference(b, c)
TypeError: symmetric_difference() takes exactly one argument (2 given)
x1.isdisjoint(x2) 判斷是否相交
- 方法: x1.isdisjoint(x2)
- 用法:確定兩個集合是否具有任何共同的元素
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'baz', 'qux', 'quux'}
>>> x1.isdisjoint(x2)
False
>>> x2 - {'baz'}
{'quux', 'qux'}
>>> x1.isdisjoint(x2 - {'baz'})
True
從這個栗子可以看出,如果兩個Set沒有共同元素返回True,如果有返回True,如果返回True同時也意味著
他們之間的交集為空集,這個很好理解:
>>> x1 = {1, 3, 5}
>>> x2 = {2, 4, 6}
>>> x1.isdisjoint(x2)
True
>>> x1 & x2
set()
注意:目前還沒有運算子對應這個方法
x1.issubset(x2) 判斷x1是否為x2子集
- 方法: x1.issubset(x2)
- 運算子:x1 <= x2
- 用法:如果返回True,x1為x2子集,反之返回False
>>> x1 = {'foo', 'bar', 'baz'}
>>> x1.issubset({'foo', 'bar', 'baz', 'qux', 'quux'})
True
>>> x2 = {'baz', 'qux', 'quux'}
>>> x1 <= x2
False
一個集合本身當然是它自己的子集啦:
>>> x = {1, 2, 3, 4, 5}
>>> x.issubset(x)
True
>>> x <= x
True
x1<x2 判斷x1是否為x2的真子集
- 運算子:x1<x2
- 用法:判斷x1是否為x2的真子集,如果返回True,x1為x2的真子集,反之返回False
首先。。。讓我們回顧一下數學知識:真子集與子集類似,除了集合不能相同。如果x1的每個元素都在x2中,並且x1和x2不相等,則集合x1被認為是另一個集合x2的真子集
換個高大上的說法也可以:如果集合A⊆B,存在元素x∈B,且元素x不屬於集合A,我們稱集合A與集合B有真包含關係,集合A是集合B的真子集(proper subset)。記作A⊊B(或B⊋A),讀作“A真包含於B”(或“B真包含A”)
>>> x1 = {'foo', 'bar'}
>>> x2 = {'foo', 'bar', 'baz'}
>>> x1 < x2
True
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'foo', 'bar', 'baz'}
>>> x1 < x2
False
雖然Set被認為是其自身的子集,但它本身並不是自己的真子集:
>>> x = {1, 2, 3, 4, 5}
>>> x <= x
True
>>> x < x
False
注意:目前還沒有方法對應這個運算子
x1.issuperset(x2) 判斷x1是否為x2的超集
- 方法:x1.issuperset(x2)
- 運算子:x1 >= x2
- 用法:判斷x1是否為x2的超集,如果是返回True,反之返回False
>>> x1 = {'foo', 'bar', 'baz'}
>>> x1.issuperset({'foo', 'bar'})
True
>>> x2 = {'baz', 'qux', 'quux'}
>>> x1 >= x2
False
我們剛才已經看到過了一個Set是它自己本身的子集,這裡也是一樣的,它同時也是自己的超集
>>> x = {1, 2, 3, 4, 5}
>>> x.issuperset(x)
True
>>> x >= x
True
x1 > x2 判斷x1是否為x2的真超集
- 運算子:x1 > x2
- 用法:判斷x1是否為x2的真超集,如果是返回True,反之返回False
真超集與超集相同,除了集合不能相同。如果x1包含x2的每個元素,並且x1和x2不相等,則集合x1被認為是另一個集合x2的真超集。
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'foo', 'bar'}
>>> x1 > x2
True
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'foo', 'bar', 'baz'}
>>> x1 > x2
False
一個集合不是它自己的真超集,和真子集的原理相同
>>> x = {1, 2, 3, 4, 5}
>>> x > x
False
對Set進行修改
雖然集合中包含的元素必須是不可變型別,但可以修改集合本身。與上面的操作類似,可以使用多種運算子和方法來更改集合的內容。
x1.update(x2) 通過union修改集合元素
- 方法:x1.update(x2[, x3 ...])
- 運算子:x1 |= x2 [| x3 ...]
- 用法:通過union修改集合
x1.update(x2) 和 x1 |= x2 作用是向集合x1中新增x2中所有x1不存在的元素。
停下3秒,我仔細讀了這句話,覺得我表達的還可以,不知道大家讀上去繞不繞,先看例子:
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'foo', 'baz', 'qux'}
>>> x1 |= x2
>>> x1
{'qux', 'foo', 'bar', 'baz'}
>>> x1.update(['corge', 'garply'])
>>> x1
{'qux', 'corge', 'garply', 'foo', 'bar', 'baz'}
x1.intersection(x2) 通過intersection修改集合元素
- 方法:x1.intersection_update(x2[, x3 ...])
- 運算子:x1 &= x2 [& x3 ...]
- 用法:通過intersection修改集合
x1.intersection_update(x2) 和 x1 &= x2 會讓x1只保留x1和x2的交集部分:
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'foo', 'baz', 'qux'}
>>> x1 &= x2
>>> x1
{'foo', 'baz'}
>>> x1.intersection_update(['baz', 'qux'])
>>> x1
{'baz'}
x1.difference_update(x2) 通過difference修改集合元素
- 方法:x1.difference_update(x2[, x3 ...])
- 運算子:x1 -= x2 [| x3 ...]
- 用法:通過difference修改集合
x1.difference_update(x2) and x1 -= x2 會讓集合x1移除所有在x2出現的屬於x1的元素:
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'foo', 'baz', 'qux'}
>>> x1 -= x2
>>> x1
{'bar'}
>>> x1.difference_update(['foo', 'bar', 'qux'])
>>> x1
set()
x1.symmetric_difference_update(x2) 通過對稱差集修改集合元素
- 方法:x1.symmetric_difference_update(x2)
- 運算子:x1 ^= x2
這個我實在用語言解釋不清了,看例子容易懂:
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'foo', 'baz', 'qux'}
>>>
>>> x1 ^= x2
>>> x1
{'bar', 'qux'}
>>>
>>> x1.symmetric_difference_update(['qux', 'corge'])
>>> x1
{'bar', 'corge'}
x.add(<elem> 新增元素
這個就很簡單了, 類似List:
>>> x = {'foo', 'bar', 'baz'}
>>> x.add('qux')
>>> x
{'bar', 'baz', 'foo', 'qux'}
x.remove(<elem>) 刪除元素
如果刪除的元素不存在會丟擲異常
>>> x = {'foo', 'bar', 'baz'}
>>> x.remove('baz')
>>> x
{'bar', 'foo'}
>>> x.remove('qux')
Traceback (most recent call last):
File "<pyshell#58>", line 1, in <module>
x.remove('qux')
KeyError: 'qux'
這個時候為了避免出現錯誤可以用discard方法
>>> x = {'foo', 'bar', 'baz'}
>>> x.discard('baz')
>>> x
{'bar', 'foo'}
>>> x.discard('qux')
>>> x
{'bar', 'foo'}
利用pop刪除隨機元素並返回:
>>> x = {'foo', 'bar', 'baz'}
>>> x.pop()
'bar'
>>> x
{'baz', 'foo'}
>>> x.pop()
'baz'
>>> x
{'foo'}
>>> x.pop()
'foo'
>>> x
set()
利用clear可以清空一個集合:
>>> x = {'foo', 'bar', 'baz'}
>>> x
{'foo', 'bar', 'baz'}
>>>
>>> x.clear()
>>> x
set()
Frozen Sets
Frozen Sets是什麼東西
Python提供了另一種稱為凍結集合Frozen Sets的內建型別,它在所有方面都與集合完全相同,只不過Frozen Sets是不可變的。我們可以對凍結集執行非修改操作,比如:
>>> x = frozenset(['foo', 'bar', 'baz'])
>>> x
frozenset({'foo', 'baz', 'bar'})
>>> len(x)
3
>>> x & {'baz', 'qux', 'quux'}
frozenset({'baz'})
如果膽敢嘗試修改Frozen Sets:
>>> x = frozenset(['foo', 'bar', 'baz'])
>>> x.add('qux')
Traceback (most recent call last):
File "<pyshell#127>", line 1, in <module>
x.add('qux')
AttributeError: 'frozenset' object has no attribute 'add'
>>> x.pop()
Traceback (most recent call last):
File "<pyshell#129>", line 1, in <module>
x.pop()
AttributeError: 'frozenset' object has no attribute 'pop'
>>> x.clear()
Traceback (most recent call last):
File "<pyshell#131>", line 1, in <module>
x.clear()
AttributeError: 'frozenset' object has no attribute 'clear'
>>> x
frozenset({'foo', 'bar', 'baz'})
基本使用舉例
Frozensets在我們想要使用集合的情況下很有用,但需要一個不可變物件。
例如,如果沒有Frozen sets我們不能定義其元素也是集合的集合(nested),因為集合元素必須是不可變的,會報錯:
>>> x1 = set(['foo'])
>>> x2 = set(['bar'])
>>> x3 = set(['baz'])
>>> x = {x1, x2, x3}
Traceback (most recent call last):
File "<pyshell#38>", line 1, in <module>
x = {x1, x2, x3}
TypeError: unhashable type: 'set'
現在有了 Frozen sets,我們有了解決方案:
>>> x1 = frozenset(['foo'])
>>> x2 = frozenset(['bar'])
>>> x3 = frozenset(['baz'])
>>> x = {x1, x2, x3}
>>> x
{frozenset({'bar'}), frozenset({'baz'}), frozenset({'foo'})}
總結
這一期為大家講了太多東西,一口老血吐在鍵盤上,總結不動了
只希望這期Set詳解介紹可以幫助到大家,如果幫到了你,就點個贊吧~~
最後再次祝大家豬年大吉!!
- x3 ... ↩