Python 提供多種內(nèi)置數(shù)據(jù)結(jié)構(gòu)用于組織數(shù)據(jù),包括列表、字典、元組和集合。
根據(jù) Python 3 文檔,集合是無序的、不包含重復(fù)元素的集合。其主要用途包括成員測試和去除重復(fù)項(xiàng)。集合還支持集合運(yùn)算,如并集、交集、差集和對稱差集。
本文將通過示例闡述以上定義中的每個特性,并講解集合的創(chuàng)建方法。
創(chuàng)建集合有兩種方法:一是將元素列表傳遞給內(nèi)置函數(shù) `set()`,二是使用花括號 `{}`。
使用 set() 函數(shù)初始化集合:
立即學(xué)習(xí)“Python免費(fèi)學(xué)習(xí)筆記(深入)”;
复制代码
- >>> s1 = set([1, 2, 3]) >>> s1 {1, 2, 3} >>> type(s1) <class 'set'>
使用 {} 初始化集合:
复制代码
- >>> s2 = {3, 4, 5} >>> s2 {3, 4, 5} >>> type(s2) <class 'set'>
兩種方法均有效。但如果需要創(chuàng)建空集合呢?
复制代码
- >>> s = {} >>> type(s) <class 'dict'>
使用空花括號將創(chuàng)建一個字典,而非集合。
為簡便起見,本文示例使用整數(shù)集合,但集合可包含所有 Python 支持的可哈希[1]數(shù)據(jù)類型,例如整數(shù)、字符串和元組,但不包括列表或字典等可變類型。
复制代码
- >>> s = {1, 'coffee', [4, 'Python']} Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unhashable type: 'list'
了解了集合的創(chuàng)建方法及其可包含的元素類型后,我們來探討一下為什么應(yīng)該將集合納入你的編程工具箱。
編程中,解決問題的方法不止一種。有些方法效率低下,而另一些則清晰、簡潔、易于維護(hù),或者更“Pythonic[2]”。
根據(jù) Python Hitchhiker’s Guide[3]:
當(dāng)經(jīng)驗(yàn)豐富的 Python 開發(fā)者(Pythonista)遇到不夠“Pythonic”的代碼時,通常認(rèn)為這些代碼不符合通用指南,表達(dá)意圖的方式不夠理想(可讀性差)。
讓我們看看集合如何提升代碼可讀性并提高程序執(zhí)行效率。
集合元素?zé)o法通過索引訪問:
复制代码
- >>> s = {1, 2, 3} >>> s[0] Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'set' object does not support indexing
也無法使用切片修改:
复制代码
- >>> s[0:2] Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'set' object is not subscriptable
但如果需要去除重復(fù)項(xiàng)或進(jìn)行集合運(yùn)算(如并集),則應(yīng)該使用集合。
迭代時,集合的效率優(yōu)于列表。原因涉及集合的內(nèi)部實(shí)現(xiàn)細(xì)節(jié),感興趣的讀者可以參考以下鏈接:
- 時間復(fù)雜度[4]
- set() 的實(shí)現(xiàn)[5]
- Python 集合 vs 列表[6]
- 列表中使用集合的優(yōu)缺點(diǎn)[7]
過去,我經(jīng)常使用 `for` 循環(huán)和 `if` 語句來檢查并去除列表中的重復(fù)元素,代碼如下:
复制代码
- >>> my_list = [1, 2, 3, 2, 3, 4] >>> no_duplicate_list = [] >>> for item in my_list: ... if item not in no_duplicate_list: ... no_duplicate_list.append(item) ... >>> no_duplicate_list [1, 2, 3, 4]
或者使用列表推導(dǎo)式:
复制代码
- >>> my_list = [1, 2, 3, 2, 3, 4] >>> no_duplicate_list = [] >>> [no_duplicate_list.append(item) for item in my_list if item not in no_duplicate_list] [None, None, None, None] >>> no_duplicate_list [1, 2, 3, 4]
現(xiàn)在,我們可以使用集合:
复制代码
- >>> my_list = [1, 2, 3, 2, 3, 4] >>> no_duplicate_list = list(set(my_list)) >>> no_duplicate_list [1, 2, 3, 4]
使用 timeit 模塊比較列表和集合去除重復(fù)項(xiàng)的效率:
复制代码
- >>> from timeit import timeit >>> def no_duplicates(list): ... no_duplicate_list = [] ... [no_duplicate_list.append(item) for item in list if item not in no_duplicate_list] ... return no_duplicate_list ... >>> # 列表的執(zhí)行時間: >>> print(timeit('no_duplicates([1, 2, 3, 1, 7])', globals=globals(), number=1000)) 0.0018683355819786227
复制代码
- >>> from timeit import timeit >>> # 集合的執(zhí)行時間: >>> print(timeit('list(set([1, 2, 3, 1, 2, 3, 4]))', number=1000)) 0.0010220493243764395 >>> # 更快更簡潔
使用集合比列表推導(dǎo)式更簡潔,效率更高。
注意:集合是無序的,轉(zhuǎn)換為列表后元素順序可能發(fā)生變化。
Python 之禪[8]:
優(yōu)美勝于丑陋
明了勝于晦澀
簡潔勝于復(fù)雜
扁平勝于嵌套
集合正是如此。
使用 `if` 語句檢查元素是否存在于列表中,稱為成員測試:
复制代码
- my_list = [1, 2, 3] >>> if 2 in my_list: ... print('Yes, this is a membership test!') ... Yes, this is a membership test!
集合的成員測試效率更高:
复制代码
- >>> from timeit import timeit >>> def in_test(iterable): ... for i in range(1000): ... if i in iterable: ... pass ... >>> timeit('in_test(iterable)', ... setup="from __main__ import in_test; iterable = list(range(1000))", ... number=1000) 12.459663048726043
复制代码
- >>> from timeit import timeit >>> def in_test(iterable): ... for i in range(1000): ... if i in iterable: ... pass ... >>> timeit('in_test(iterable)', ... setup="from __main__ import in_test; iterable = set(range(1000))", ... number=1000) .12354438152988223
注意:以上測試來自 StackOverflow[9]。
對于大型列表,將其轉(zhuǎn)換為集合可以提高成員測試效率。
了解了集合及其用途后,我們來快速瀏覽一下集合的修改和操作方法。
根據(jù)需要添加的元素?cái)?shù)量,選擇 `add()` 或 `update()` 方法。
add() 用于添加單個元素:
复制代码
- >>> s = {1, 2, 3} >>> s.add(4) >>> s {1, 2, 3, 4}
update() 用于添加多個元素:
复制代码
- >>> s = {1, 2, 3} >>> s.update([2, 3, 4, 5, 6]) >>> s {1, 2, 3, 4, 5, 6}
集合會自動去除重復(fù)元素。
如果需要在刪除不存在的元素時引發(fā)異常,使用 `remove()`;否則,使用 `discard()`:
复制代码
- >>> s = {1, 2, 3} >>> s.remove(3) >>> s {1, 2} >>> s.remove(3) Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 3
discard() 不會引發(fā)異常:
复制代码
- >>> s = {1, 2, 3} >>> s.discard(3) >>> s {1, 2} >>> s.discard(3) >>> # 無任何操作
pop() 用于隨機(jī)移除一個元素:
复制代码
- >>> s = {1, 2, 3, 4, 5} >>> s.pop() # 移除一個任意元素 1 >>> s {2, 3, 4, 5}
clear() 用于清空集合:
复制代码
- >>> s = {1, 2, 3, 4, 5} >>> s.clear() # 清空集合 >>> s set()
`union()` 或 `|` 操作符創(chuàng)建包含所有元素的新集合:
复制代码
- >>> s1 = {1, 2, 3} >>> s2 = {3, 4, 5} >>> s1.union(s2) # 或 's1 | s2' {1, 2, 3, 4, 5}
`intersection()` 或 `&` 操作符返回集合的公共元素:
复制代码
- >>> s1 = {1, 2, 3} >>> s2 = {2, 3, 4} >>> s3 = {3, 4, 5} >>> s1.intersection(s2, s3) # 或 's1 & s2 & s3' {3}
`difference()` 或 `-` 操作符創(chuàng)建新集合,包含在 `s1` 中但在 `s2` 中不存在的元素:
复制代码
- >>> s1 = {1, 2, 3} >>> s2 = {2, 3, 4} >>> s1.difference(s2) # 或 's1 - s2' {1}
`symmetric_difference()` 或 `^` 操作符返回集合中不同的元素:
复制代码
- >>> s1 = {1, 2, 3} >>> s2 = {2, 3, 4} >>> s1.symmetric_difference(s2) # 或 's1 ^ s2' {1, 4}
本文介紹了 Python 集合的概念、操作方法和應(yīng)用場景。熟練掌握集合的使用可以編寫更清晰、更高效的代碼。
如有任何疑問,請隨時提出。此外,Python 速查表[10]中也包含了集合的相關(guān)內(nèi)容[11],方便查閱。