一. 集合論
集合的本質是許多唯一對象的聚集,因此,集合可以用於去重:
集合中的元素必須是可散列的,set 類型本身是不可散列的,但是 frozenset 可以。除了保證唯一性,集合還實現了很多基礎的中綴運算符。給定兩個集合 a 和 b,a | b 返回的是它們的合集,a & b 得到的是交集,而 a - b 得到的是差集。
演示1 needles 的元素在 haystack 裏出現的次數,兩個變量都是 set 類型
演示2 needles 的元素在 haystack 裏出現的次數,這次的代碼可以用在任何可迭代對象上
以上的所有例子的運行時間都能在 3 毫秒左右,在含有 10 000 000 個元素的 haystack 裏搜索 1000 個值,算下來大概是每個元素 3 微秒。除了速度極快的查找功能(這也得歸功於它背後的散列表),內置的 set 和 frozenset 提供了豐富的功能和操作,不但讓創建集合的方式豐富多彩,而且對於 set 來講,我們還可以對集合裏已有的元素進行修改。
1.1 集合字面量
除空集之外,集合的字面量——{1}、{1, 2},等等——看起來跟它的數學形式一模一樣。如果是空集,那麼必須寫成 set() 的形式,如果只是寫成 {} 的形式,跟以前一樣,你創建的其實是個空字典。
像 {1, 2, 3} 這種字面量句法相比於構造方法(set([1, 2, 3]))要更快且更易讀。用 dis.dis(反彙編函數)來看看兩個方法的字節碼的不同:
特殊的字節碼 BUILD_SET 幾乎完成了所有的工作。而使用set()創建時,3 種不同的操作代替了上面的 BUILD_SET:LOAD_NAME、BUILD_LIST 和 CALL_FUNCTION。
由於 Python 裏沒有針對 frozenset 的特殊字面量句法,我們只能採用構造方法:
1.2 集合推導
演示2 新建一個 Latin-1 字符集合,該集合裏的每個字符的 Unicode 名字裏都有“SIGN”這個單詞
從 unicodedata 模塊裏導入 name 函數,用以獲取字符的名字。把編碼在 32~255 之間的字符的名字裏有“SIGN”單詞的挑出來,放到一個集合裏。
1.3 集合的操作
列出了可變和不可變集合所擁有的方法的概況,其中不少是運算符重載的特殊方法。
中綴運算符需要兩側的被操作對象都是集合類型,但是其他的所有方法則只要求所傳入的參數是可迭代對象。例如,想求 4 個聚合類型 a、b、c 和 d 的合集,可以用 a.union(b, c, d),這裏 a 必須是個 set,但是 b、c 和 d 則可以是任何類型的可迭代對象。
表 1-1:集合的數學運算:這些方法或者會生成新集合,或者會在條件允許的情況下就地修改集合
數學符號 | Python運算符 | 方法 | 描述 |
S ∩ Z | s & z | s.__add__(z) |
s 和 z 的交集
|
z & s | s.__radd__(z) |
反向 & 操作
|
|
s.intersection(it, ...)
|
把可迭代的 it 和其他所有參數轉化爲集合,然後求它們與 s 的交集
|
||
s &= z
|
s.__iand__(z) |
把 s 更新爲 s 和 z 的交集
|
|
s.intersection_update(it, ...)
|
把可迭代的 it 和其他所有參數轉化爲集合,然後求得它們與 s 的交集,然後把 s 更新成這個交集
|
||
S ∪ Z
|
s | z
|
s.__or__(z) |
s 和 z 的並集
|
z | s
|
s.__ror__(z) |
| 的反向操作
|
|
s.union(it, ...) |
把可迭代的 it 和其他所有參數轉化爲集合,然後求它們和 s 的並集
|
||
s |= z
|
s.__ior__(z) |
把 s 更新爲 s 和 z 的並集
|
|
s.update(it, ...) |
把可迭代的 it 和其他所有參數轉化爲集合,然後求它們和 s 的並集,並把 s 更新成這個並集
|
||
S \ Z
|
s - z | s.__sub__(z) |
s 和 z 的差集,或者叫作相對補集
|
z - s | s.__rsub__(z) |
- 的反向操作
|
|
s.difference(it, ...)
|
把可迭代的 it 和其他所有參數轉化爲集合,然後求它們和 s 的差集
|
||
s -= z | s.__isub__(z) |
把 s 更新爲它與 z 的差集
|
|
s.difference_update(it, ...)
|
把可迭代的 it 和其他所有參數轉化爲集合,求它們和 s 的差集,然後把 s 更新成這個差集
|
||
s.symmetric_difference(it)
|
求 s 和 set(it) 的對稱差集
|
||
S △ Z
|
s ^ z
|
s.__xor__(z) |
求 s 和 z 的對稱差集
|
z ^ s
|
s.__rxor__(z) |
^ 的反向操作
|
|
s.symmetric_difference_update(it, ...)
|
把可迭代的 it 和其他所有參數轉化爲集合,然後求它們和 s 的對稱差集,最後把 s 更新成該結果
|
||
s ^= z
|
s.__ixor__(z) |
把 s 更新成它與 z 的對稱差集
|
表 1-2:集合的比較運算符,返回值是布爾類型
數學符號 | Python運算符 | 方法 | 描述 |
—— | —— | s.isdisjoint(z) |
查看 s 和 z 是否不相交(沒有共同元素)
|
e ∈ S
|
e in s | s.__contains__(e) |
元素 e 是否屬於 s
|
S ⊆ Z
|
s <= z | s.__le__(z) |
s 是否爲 z 的子集
|
s.issubset(it) |
把可迭代的 it 轉化爲集合,然後查看 s 是否爲它的子集
|
||
S ⊂ Z
|
s < z | s.__lt__(z) |
s 是否爲 z 的真子集
|
S ⊇ Z
|
s >= z | s.__ge__(z) |
s 是否爲 z 的父集
|
s.issuperset(it)
|
把可迭代的 it 轉化爲集合,然後查看 s 是否爲它的父集
|
||
S ⊃ Z
|
s > z | s.__gt__(z) |
s 是否爲 z 的真父集
|
表 1-3:集合類型的其他方法
方法 | set | frozenset | 描述 |
s.add(e) | √ | × |
把元素 e 添加到 s 中
|
s.clear()
|
√ | × |
移除掉 s 中的所有元素
|
s.copy() | √ | √ |
對 s 淺複製
|
s.discard(e)
|
√ | × |
如果 s 裏有 e 這個元素的話,把它移除
|
s.__iter__()
|
√ | √ |
返回 s 的迭代器
|
s.__len__()
|
√ | √ | len(s) |
s.pop()
|
√ | × |
從 s 中移除一個元素並返回它的值,若 s 爲空,則拋出 KeyError 異常
|
s.remove(e)
|
√ | × |
從 s 中移除 e 元素,若 e 元素不存在,則拋出 KeyError 異常
|