
數(shù)據(jù)結(jié)構(gòu)
數(shù)據(jù)結(jié)構(gòu)的概念很好理解,就是用來將數(shù)據(jù)組織在一起的結(jié)構(gòu)。換句話說,數(shù)據(jù)結(jié)構(gòu)是用來存儲(chǔ)一系列關(guān)聯(lián)數(shù)據(jù)的東西。在Python中有四種內(nèi)建的數(shù)據(jù)結(jié)構(gòu),分別是List、Tuple、Dictionary以及Set。大部分的應(yīng)用程序不需要其他類型的數(shù)據(jù)結(jié)構(gòu),但若是真需要也有很多高級(jí)數(shù)據(jù)結(jié)構(gòu)可供選擇,例如Collection、Array、Heapq、Bisect、Weakref、Copy以及Pprint。本文將介紹這些數(shù)據(jù)結(jié)構(gòu)的用法,看看它們是如何幫助我們的應(yīng)用程序的。
關(guān)于四種內(nèi)建數(shù)據(jù)結(jié)構(gòu)的使用方法很簡(jiǎn)單,并且網(wǎng)上有很多參考資料,因此本文將不會(huì)討論它們。
1. Collections
collections模塊包含了內(nèi)建類型之外的一些有用的工具,例如Counter、defaultdict、OrderedDict、deque以及nametuple。其中Counter、deque以及defaultdict是最常用的類。
1.1 Counter()
如果你想統(tǒng)計(jì)一個(gè)單詞在給定的序列中一共出現(xiàn)了多少次,諸如此類的操作就可以用到Counter。來看看如何統(tǒng)計(jì)一個(gè)list中出現(xiàn)的item次數(shù):
若要統(tǒng)計(jì)一個(gè)list中不同單詞的數(shù)目,可以這么用:
如果需要對(duì)結(jié)果進(jìn)行分組,可以這么做:
以下的代碼片段找出一個(gè)字符串中出現(xiàn)頻率最高的單詞,并打印其出現(xiàn)次數(shù)。
1.2 Deque
Deque是一種由隊(duì)列結(jié)構(gòu)擴(kuò)展而來的雙端隊(duì)列(double-ended queue),隊(duì)列元素能夠在隊(duì)列兩端添加或刪除。因此它還被稱為頭尾連接列表(head-tail linked list),盡管叫這個(gè)名字的還有另一個(gè)特殊的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)。
Deque支持線程安全的,經(jīng)過優(yōu)化的append和pop操作,在隊(duì)列兩端的相關(guān)操作都能夠達(dá)到近乎O(1)的時(shí)間復(fù)雜度。雖然list也支持類似的操作,但是它是對(duì)定長(zhǎng)列表的操作表現(xiàn)很不錯(cuò),而當(dāng)遇到pop(0)和insert(0, v)這樣既改變了列表的長(zhǎng)度又改變其元素位置的操作時(shí),其復(fù)雜度就變?yōu)镺(n)了。
來看看相關(guān)的比較結(jié)果:
另一個(gè)例子是執(zhí)行基本的隊(duì)列操作:
譯者注:rotate是隊(duì)列的旋轉(zhuǎn)操作,Right rotate(正參數(shù))是將右端的元素移動(dòng)到左端,而Left rotate(負(fù)參數(shù))則相反。
1.3 Defaultdict
這個(gè)類型除了在處理不存在的鍵的操作之外與普通的字典完全相同。當(dāng)查找一個(gè)不存在的鍵操作發(fā)生時(shí),它的default_factory會(huì)被調(diào)用,提供一個(gè)默認(rèn)的值,并且將這對(duì)鍵值存儲(chǔ)下來。其他的參數(shù)同普通的字典方法dict()一致,一個(gè)defaultdict的實(shí)例同內(nèi)建dict一樣擁有同樣地操作。
defaultdict對(duì)象在當(dāng)你希望使用它存放追蹤數(shù)據(jù)的時(shí)候很有用。舉個(gè)例子,假定你希望追蹤一個(gè)單詞在字符串中的位置,那么你可以這么做:
是選擇lists或sets與defaultdict搭配取決于你的目的,使用list能夠保存你插入元素的順序,而使用set則不關(guān)心元素插入順序,它會(huì)幫助消除重復(fù)元素。
另一種創(chuàng)建multidict的方法:
一個(gè)更復(fù)雜的例子:
2. Array
array模塊定義了一個(gè)很像list的新對(duì)象類型,不同之處在于它限定了這個(gè)類型只能裝一種類型的元素。array元素的類型是在創(chuàng)建并使用的時(shí)候確定的。
如果你的程序需要優(yōu)化內(nèi)存的使用,并且你確定你希望在list中存儲(chǔ)的數(shù)據(jù)都是同樣類型的,那么使用array模塊很合適。舉個(gè)例子,如果需要存儲(chǔ)一千萬個(gè)整數(shù),如果用list,那么你至少需要160MB的存儲(chǔ)空間,然而如果使用array,你只需要40MB。但雖然說能夠節(jié)省空間,array上幾乎沒有什么基本操作能夠比在list上更快。
在使用array進(jìn)行計(jì)算的時(shí)候,需要特別注意那些創(chuàng)建list的操作。例如,使用列表推導(dǎo)式(list comprehension)的時(shí)候,會(huì)將array整個(gè)轉(zhuǎn)換為list,使得存儲(chǔ)空間膨脹。一個(gè)可行的替代方案是使用生成器表達(dá)式創(chuàng)建新的array。看代碼:
因?yàn)槭褂胊rray是為了節(jié)省空間,所以更傾向于使用in-place操作。一種更高效的方法是使用enumerate:
對(duì)于較大的array,這種in-place修改能夠比用生成器創(chuàng)建一個(gè)新的array至少提升15%的速度。
那么什么時(shí)候使用array呢?是當(dāng)你在考慮計(jì)算的因素之外,還需要得到一個(gè)像C語言里一樣統(tǒng)一元素類型的數(shù)組時(shí)。
3.Heapq
heapq模塊使用一個(gè)用堆實(shí)現(xiàn)的優(yōu)先級(jí)隊(duì)列。堆是一種簡(jiǎn)單的有序列表,并且置入了堆的相關(guān)規(guī)則。
堆是一種樹形的數(shù)據(jù)結(jié)構(gòu),樹上的子節(jié)點(diǎn)與父節(jié)點(diǎn)之間存在順序關(guān)系。二叉堆(binary heap)能夠用一個(gè)經(jīng)過組織的列表或數(shù)組結(jié)構(gòu)來標(biāo)識(shí),在這種結(jié)構(gòu)中,元素N的子節(jié)點(diǎn)的序號(hào)為2*N+1和2*N+2(下標(biāo)始于0)。簡(jiǎn)單來說,這個(gè)模塊中的所有函數(shù)都假設(shè)序列是有序的,所以序列中的第一個(gè)元素(seq[0])是最小的,序列的其他部分構(gòu)成一個(gè)二叉樹,并且seq[i]節(jié)點(diǎn)的子節(jié)點(diǎn)分別為seq[2*i+1]以及seq[2*i+2]。當(dāng)對(duì)序列進(jìn)行修改時(shí),相關(guān)函數(shù)總是確保子節(jié)點(diǎn)大于等于父節(jié)點(diǎn)。
heapq模塊有兩個(gè)函數(shù)nlargest()和nsmallest(),顧名思義,讓我們來看看它們的用法。
兩個(gè)函數(shù)也能夠通過一個(gè)鍵參數(shù)使用更為復(fù)雜的數(shù)據(jù)結(jié)構(gòu),例如:
來看看如何實(shí)現(xiàn)一個(gè)根據(jù)給定優(yōu)先級(jí)進(jìn)行排序,并且每次pop操作都返回優(yōu)先級(jí)最高的元素的隊(duì)列例子。
4. Bisect
bisect模塊能夠提供保持list元素序列的支持。它使用了二分法完成大部分的工作。它在向一個(gè)list插入元素的同時(shí)維持list是有序的。在某些情況下,這比重復(fù)的對(duì)一個(gè)list進(jìn)行排序更為高效,并且對(duì)于一個(gè)較大的list來說,對(duì)每步操作維持其有序也比對(duì)其排序要高效。
假設(shè)你有一個(gè)range集合:
如果我想添加一個(gè)range (250, 400),我可能會(huì)這么做:
我們可以使用bisect()函數(shù)來尋找插入點(diǎn):
bisect(sequence, item) => index 返回元素應(yīng)該的插入點(diǎn),但序列并不被修改。
新元素被插入到第5的位置。
5. Weakref
weakref模塊能夠幫助我們創(chuàng)建Python引用,卻不會(huì)阻止對(duì)象的銷毀操作。這一節(jié)包含了weak reference的基本用法,并且引入一個(gè)代理類。
在開始之前,我們需要明白什么是strong reference。strong reference是一個(gè)對(duì)對(duì)象的引用次數(shù)、生命周期以及銷毀時(shí)機(jī)產(chǎn)生影響的指針。strong reference如你所見,就是當(dāng)你將一個(gè)對(duì)象賦值給一個(gè)變量的時(shí)候產(chǎn)生的:
在這種情況下,這個(gè)列表有兩個(gè)strong reference,分別是a和b。在這兩個(gè)引用都被釋放之前,這個(gè)list不會(huì)被銷毀。
Weak reference則是對(duì)對(duì)象的引用計(jì)數(shù)器不會(huì)產(chǎn)生影響。當(dāng)一個(gè)對(duì)象存在weak reference時(shí),并不會(huì)影響對(duì)象的撤銷。這就說,如果一個(gè)對(duì)象僅剩下weak reference,那么它將會(huì)被銷毀。
你可以使用weakref.ref函數(shù)來創(chuàng)建對(duì)象的weak reference。這個(gè)函數(shù)調(diào)用需要將一個(gè)strong reference作為第一個(gè)參數(shù)傳給函數(shù),并且返回一個(gè)weak reference。
一個(gè)臨時(shí)的strong reference可以從weak reference中創(chuàng)建,即是下例中的b():
請(qǐng)注意當(dāng)我們刪除strong reference的時(shí)候,對(duì)象將立即被銷毀。
如果試圖在對(duì)象被摧毀之后通過weak reference使用對(duì)象,則會(huì)返回None:
若是使用weakref.proxy,就能提供相對(duì)于weakref.ref更透明的可選操作。同樣是使用一個(gè)strong reference作為第一個(gè)參數(shù)并且返回一個(gè)weak reference,proxy更像是一個(gè)strong reference,但當(dāng)對(duì)象不存在時(shí)會(huì)拋出異常。
完整的例子:
引用計(jì)數(shù)器是由Python的垃圾回收器使用的,當(dāng)一個(gè)對(duì)象的應(yīng)用計(jì)數(shù)器變?yōu)?,則其將會(huì)被垃圾回收器回收。
最好將weak reference用于開銷較大的對(duì)象,或避免循環(huán)引用(雖然垃圾回收器經(jīng)常干這種事情)。
提示:只有l(wèi)ibrary模塊中定義的class instances、functions、methods、sets、frozen sets、files、generators、type objects和certain object types(例如sockets、arrays和regular expression patterns)支持weakref。內(nèi)建函數(shù)以及大部分內(nèi)建類型如lists、dictionaries、strings和numbers則不支持。
6. Copy()
通過shallow或deep copy語法提供復(fù)制對(duì)象的函數(shù)操作。
shallow和deep copying的不同之處在于對(duì)于混合型對(duì)象的操作(混合對(duì)象是包含了其他類型對(duì)象的對(duì)象,例如list或其他類實(shí)例)。
1.對(duì)于shallow copy而言,它創(chuàng)建一個(gè)新的混合對(duì)象,并且將原對(duì)象中其他對(duì)象的引用插入新對(duì)象。
2.對(duì)于deep copy而言,它創(chuàng)建一個(gè)新的對(duì)象,并且遞歸地復(fù)制源對(duì)象中的其他對(duì)象并插入新的對(duì)象中。
普通的賦值操作知識(shí)簡(jiǎn)單的將心變量指向源對(duì)象。
shallow copy (copy())操作創(chuàng)建一個(gè)新的容器,其包含的引用指向原對(duì)象中的對(duì)象。
deep copy (deepcopy())創(chuàng)建的對(duì)象包含的引用指向復(fù)制出來的新對(duì)象。
復(fù)雜的例子:
假定我有兩個(gè)類,名為Manager和Graph,每個(gè)Graph包含了一個(gè)指向其manager的引用,而每個(gè)Manager有一個(gè)指向其管理的Graph的集合,現(xiàn)在我們有兩個(gè)任務(wù)需要完成:
1) 復(fù)制一個(gè)graph實(shí)例,使用deepcopy,但其manager指向?yàn)樵璯raph的manager。
2) 復(fù)制一個(gè)manager,完全創(chuàng)建新manager,但拷貝原有的所有g(shù)raph。
7. Pprint()
Pprint模塊能夠提供比較優(yōu)雅的數(shù)據(jù)結(jié)構(gòu)打印方式,如果你需要打印一個(gè)結(jié)構(gòu)較為復(fù)雜,層次較深的字典或是JSON對(duì)象時(shí),使用Pprint能夠提供較好的打印結(jié)果。
假定你需要打印一個(gè)矩陣,當(dāng)使用普通的print時(shí),你只能打印出普通的列表,不過如果使用pprint,你就能打出漂亮的矩陣結(jié)構(gòu)
如果
額外的知識(shí)
一些基本的數(shù)據(jù)結(jié)構(gòu)
1. 單鏈鏈表
2. 用Python實(shí)現(xiàn)的普林姆算法
譯者注:普林姆算法(Prims Algorithm)是圖論中,在加權(quán)連通圖中搜索最小生成樹的算法。
總結(jié)
如果想了解更多地?cái)?shù)據(jù)結(jié)構(gòu)信息請(qǐng)參閱相關(guān)文檔。謝謝閱讀。
數(shù)據(jù)分析咨詢請(qǐng)掃描二維碼
若不方便掃碼,搜微信號(hào):CDAshujufenxi
SQL Server 中 CONVERT 函數(shù)的日期轉(zhuǎn)換:從基礎(chǔ)用法到實(shí)戰(zhàn)優(yōu)化 在 SQL Server 的數(shù)據(jù)處理中,日期格式轉(zhuǎn)換是高頻需求 —— 無論 ...
2025-09-18MySQL 大表拆分與關(guān)聯(lián)查詢效率:打破 “拆分必慢” 的認(rèn)知誤區(qū) 在 MySQL 數(shù)據(jù)庫管理中,“大表” 始終是性能優(yōu)化繞不開的話題。 ...
2025-09-18CDA 數(shù)據(jù)分析師:表結(jié)構(gòu)數(shù)據(jù) “獲取 - 加工 - 使用” 全流程的賦能者 表結(jié)構(gòu)數(shù)據(jù)(如數(shù)據(jù)庫表、Excel 表、CSV 文件)是企業(yè)數(shù)字 ...
2025-09-18DSGE 模型中的 Et:理性預(yù)期算子的內(nèi)涵、作用與應(yīng)用解析 動(dòng)態(tài)隨機(jī)一般均衡(Dynamic Stochastic General Equilibrium, DSGE)模 ...
2025-09-17Python 提取 TIF 中地名的完整指南 一、先明確:TIF 中的地名有哪兩種存在形式? 在開始提取前,需先判斷 TIF 文件的類型 —— ...
2025-09-17CDA 數(shù)據(jù)分析師:解鎖表結(jié)構(gòu)數(shù)據(jù)特征價(jià)值的專業(yè)核心 表結(jié)構(gòu)數(shù)據(jù)(以 “行 - 列” 規(guī)范存儲(chǔ)的結(jié)構(gòu)化數(shù)據(jù),如數(shù)據(jù)庫表、Excel 表、 ...
2025-09-17Excel 導(dǎo)入數(shù)據(jù)含缺失值?詳解 dropna 函數(shù)的功能與實(shí)戰(zhàn)應(yīng)用 在用 Python(如 pandas 庫)處理 Excel 數(shù)據(jù)時(shí),“缺失值” 是高頻 ...
2025-09-16深入解析卡方檢驗(yàn)與 t 檢驗(yàn):差異、適用場(chǎng)景與實(shí)踐應(yīng)用 在數(shù)據(jù)分析與統(tǒng)計(jì)學(xué)領(lǐng)域,假設(shè)檢驗(yàn)是驗(yàn)證研究假設(shè)、判斷數(shù)據(jù)差異是否 “ ...
2025-09-16CDA 數(shù)據(jù)分析師:掌控表格結(jié)構(gòu)數(shù)據(jù)全功能周期的專業(yè)操盤手 表格結(jié)構(gòu)數(shù)據(jù)(以 “行 - 列” 存儲(chǔ)的結(jié)構(gòu)化數(shù)據(jù),如 Excel 表、數(shù)據(jù) ...
2025-09-16MySQL 執(zhí)行計(jì)劃中 rows 數(shù)量的準(zhǔn)確性解析:原理、影響因素與優(yōu)化 在 MySQL SQL 調(diào)優(yōu)中,EXPLAIN執(zhí)行計(jì)劃是核心工具,而其中的row ...
2025-09-15解析 Python 中 Response 對(duì)象的 text 與 content:區(qū)別、場(chǎng)景與實(shí)踐指南 在 Python 進(jìn)行 HTTP 網(wǎng)絡(luò)請(qǐng)求開發(fā)時(shí)(如使用requests ...
2025-09-15CDA 數(shù)據(jù)分析師:激活表格結(jié)構(gòu)數(shù)據(jù)價(jià)值的核心操盤手 表格結(jié)構(gòu)數(shù)據(jù)(如 Excel 表格、數(shù)據(jù)庫表)是企業(yè)最基礎(chǔ)、最核心的數(shù)據(jù)形態(tài) ...
2025-09-15Python HTTP 請(qǐng)求工具對(duì)比:urllib.request 與 requests 的核心差異與選擇指南 在 Python 處理 HTTP 請(qǐng)求(如接口調(diào)用、數(shù)據(jù)爬取 ...
2025-09-12解決 pd.read_csv 讀取長(zhǎng)浮點(diǎn)數(shù)據(jù)的科學(xué)計(jì)數(shù)法問題 為幫助 Python 數(shù)據(jù)從業(yè)者解決pd.read_csv讀取長(zhǎng)浮點(diǎn)數(shù)據(jù)時(shí)的科學(xué)計(jì)數(shù)法問題 ...
2025-09-12CDA 數(shù)據(jù)分析師:業(yè)務(wù)數(shù)據(jù)分析步驟的落地者與價(jià)值優(yōu)化者 業(yè)務(wù)數(shù)據(jù)分析是企業(yè)解決日常運(yùn)營(yíng)問題、提升執(zhí)行效率的核心手段,其價(jià)值 ...
2025-09-12用 SQL 驗(yàn)證業(yè)務(wù)邏輯:從規(guī)則拆解到數(shù)據(jù)把關(guān)的實(shí)戰(zhàn)指南 在業(yè)務(wù)系統(tǒng)落地過程中,“業(yè)務(wù)邏輯” 是連接 “需求設(shè)計(jì)” 與 “用戶體驗(yàn) ...
2025-09-11塔吉特百貨孕婦營(yíng)銷案例:數(shù)據(jù)驅(qū)動(dòng)下的精準(zhǔn)零售革命與啟示 在零售行業(yè) “流量紅利見頂” 的當(dāng)下,精準(zhǔn)營(yíng)銷成為企業(yè)突圍的核心方 ...
2025-09-11CDA 數(shù)據(jù)分析師與戰(zhàn)略 / 業(yè)務(wù)數(shù)據(jù)分析:概念辨析與協(xié)同價(jià)值 在數(shù)據(jù)驅(qū)動(dòng)決策的體系中,“戰(zhàn)略數(shù)據(jù)分析”“業(yè)務(wù)數(shù)據(jù)分析” 是企業(yè) ...
2025-09-11Excel 數(shù)據(jù)聚類分析:從操作實(shí)踐到業(yè)務(wù)價(jià)值挖掘 在數(shù)據(jù)分析場(chǎng)景中,聚類分析作為 “無監(jiān)督分組” 的核心工具,能從雜亂數(shù)據(jù)中挖 ...
2025-09-10統(tǒng)計(jì)模型的核心目的:從數(shù)據(jù)解讀到?jīng)Q策支撐的價(jià)值導(dǎo)向 統(tǒng)計(jì)模型作為數(shù)據(jù)分析的核心工具,并非簡(jiǎn)單的 “公式堆砌”,而是圍繞特定 ...
2025-09-10