深入理解python之二——python列表和元組
從一開始學習python的時候,很多人就聽到的是元組和列表差不多,區別就是元組不可以改變,列表可以改變。
從數據結構來說,這兩者都應當屬於數組,元組屬於靜態的數組,而列表屬於動態數組。稍後再內存的分配上也會體現這一點。對於這兩種類型,除了能接受相同類型的值之外,也接受混合類型的值。
元組
說元組不能改變的,其實只是看到了操作結果顯示出來的表面現象。可以來看一下下列的操作(輸出結果截去了相同的高幾位)。
a = (1,2,3) b = (1,2,3) c = a+b id(a) >>>71845752 id(b) >>>99964864 id(c) >>>97883880
這樣的結果意味著,對於元組,即便元組組成的值是相同的,每次綁定一個變量都是重新開辟空間生成一個新的。在執行a+b的元組組合操作時,也是在重新創建一個新的元組,而不是在前一個的基礎上拼接的。也就是說所有的內容都會被復制一份,所以這裏的復雜度不是O(1),而是O(n)。
此外,元組還有一個額外要註意的細節。在《Python高性能編程》一書中作者提到了1-20長度的小元組在被回收之後不會返還給操作系統處理,而是在程序內保留。這一細節可能是根據python2.7來說的,對於python3.x版本未驗證。元組一般會緩存在Python的運行時環境中,使用的時候不用去訪問內核來分配內存。
列表
列表主要的操作時增刪,切片,查找,排序。前兩者依賴於列表的組織結構。由於列表是可變的,所以列表除了本身的數據之外,每個數據單元還會額外消耗一個單位的單元用於保存結構信息,以便列表進行重新調整大小。所以假如同樣保存10W數據,列表實際占用的數據單元是遠大於10W這個數。
對於列表的增加,主要是一個append()方法,涉及一個動態申請空間的算法。每次使用.append()操作的時候就會申請空間,申請總是按照一定的算法申請遠不止一個空間,便於接下來的擴展。這點在初學數據結構的時候可能都會知道,一次申請最好申請一個差不多的,這要比一個一個申請更有效,不過具體數量是多少,這可能是一個奧卡姆剃刀的問題。預測式的空間申請算法,會帶來一定的額外空間開銷。
列表的.index()方法提供了一種搜索調用,但它的實現是線性搜索,效率並不高。
列表的排序方法使用的是Tim排序,相對來說是一種比較好的實現。
列表在加載的時候,相對於元組的速度也是慢了許多。
最後
對於兩者其實都可以通過自己編寫安排引入算法來進行搜索或排序。如果自己編寫有點困難,也可以借助內建的bisect模塊。
深入理解python之二——python列表和元組