Fluent Python 筆記(三):高效操作序列
目錄:
Fluent Python 筆記(一):數據模型
Fluent Python 筆記(二):序列基礎
Fluent Python 筆記(三):高效操作序列
Fluent Python 筆記(四):字典和集合
Fluent Python 筆記(五):把函數當做對象
本篇主要講序列的通用操作,如切片,運算符。以及序列的排序,搜索。還有一些遇到性能瓶頸時,用於取代 list 的數據結構。
切片(Slice)
切片是序列中非常常用的操作,基本格式是 seq[start:stop:step],其中step及之前的冒號均可以省略,表示 step 為1。start和stop也都可以省略,但是第一個冒號不能省略。start省略表示從頭開始,stop省略表示切到尾部。
切片操作適用於幾乎所有序列類型,如 list, tuple, str 等等。
示例:
>>> l = [10, 20, 30, 40, 50, 60]n>>> l[:2] # split at 2n[10, 20]n>>> l[2:]n[30, 40, 50, 60]n>>> l[2:4]n[30, 40]n
Python 中表示範圍一般使用前閉後開 [start, stop),根據這兩個單詞也比較容易記憶。
切片的長度,自然也就是 stop - start。
切片對象:
符號 a:b:c 只有在 [] 中作為索引使用時才有意義,產生了一個切片對象:slice(a, b, c)。
在計算 seq[start:stop:step]時,Python 調用 seq.__getitem__(slice(start, stop, step))。
切片對象是可以命名的(實際上 Python 中的賦值都是把名稱綁定到對象上):
>>> MIDDLE = slice(2:4)n>>> l[MIDDLE]n[30, 40]n
多維切片:
很少會用到,但需要記住的是:
計算 a[i, j] 時,Python 調用 a.__getitem__((i, j))。
切片賦值:
我們可以把可變序列中的某個條目替換為其他值,當然也可以把其中的一個切片對應的部分替換為另一個序列,長度可以不同。也可以把這部分刪除。
示例:
>>> l = list(range(10))n>>> ln[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]n>>> l[2:5] = [20, 30]n>>> ln[0, 1, 20, 30, 5, 6, 7, 8, 9]n>>> del l[5:7]n>>> ln[0, 1, 20, 30, 5, 8, 9]n>>> l[3::2] = [11, 22]n>>> ln[0, 1, 20, 11, 5, 22, 9]n>>> l[2:5] = 100nTraceback (most recent call last):nFile "<stdin>", line 1, in <module>nTypeError: can only assign an iterablen>>> l[2:5] = [100]n>>> ln[0, 1, 100, 22, 9]n
需要注意:
- 不可變序列不能這樣賦值。(這是很顯然的)
- 給切片賦值的時候,只能用序列。(不能用單個條目)
序列的運算符操作
加法示例:
>>> [1, 2] + [3, 4, 5]n[1, 2, 3, 4, 5]n
加號兩邊的序列類型必須相同。
乘法示例:
>>> l = [1, 2, 3]n>>> l * 5n[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]n>>> 5 * abcdnabcdabcdabcdabcdabcdn
加法和乘法都是創建一個新的序列,之前的序列都不會修改。
複合賦值:
在執行 += 的時候, Python 會首先檢查 __iadd__ 函數有沒有實現,如果沒有,則 fall back 到 __add__ 。
執行 a += b 後,對於可變序列,a 的地址不會改變,對於不可變序列,a 的地址會改變。
排序
對於 list 來說,有兩種排序方式:
- list.sort
對一個 list 就地排序,並返回 None 來提醒我們沒有創建新的 list
(這是一個重要的 API 慣例:就地改變對象的函數和方法應該返回 None,從而明確地告訴調用者對象本身改變了,而不是創建了新的對象)
示例:
>>> fruits = [grape, raspberry, apple, banana]n>>> fruits.sort()n>>> fruitsn[apple, banana, grape, raspberry]n
- sorted 函數
應用於一個列表上,併產生一個新的,已排序的列表,不改變之前的列表。
管理有序序列
在經過上面的方式排序以後,常用的操作有查找和插入。
這兩個操作都使用 bisect 模塊。
- 使用 bisect.bisect_left 和 bisect.bisect_right 進行搜索
這兩個函數都可以在一個有序序列中搜索 target。
- 使用 bisect.insort 進行插入操作
排序是比較昂貴的操作,如果已經有了排好序的序列,可以用這個函數保持有序。
(這兩個函數具體使用的時候查閱文檔,常用的話就記住,不常用就了解下好了)
什麼時候不該使用 list ?
由於 list 是一個非常方便的結構,寫程序時不由自主就會首先考慮它,大部分情況下是沒什麼問題的,然而如果遇到了性能瓶頸,就需要考慮使用其他數據結構來替代 list。
- 如果存的全是數字,那麼使用 array.array
如果需要大量操作數字,請使用 memoryview。
如果還不夠,請使用科學計算庫 NumPy 和 SciPy。
- 如果經常從兩端添加刪除元素,請使用 collections.deque
deque 是一個線程安全的雙端隊列,設計用來快速從兩端插入刪除。
可以設定最大長度,插入的時候如果超出了最大長度,就從對面忽略元素。
deque 實現了大部分 list 的方法,並添加了一些如 popleft 和 rotate。
append 和 popleft 都是原子操作,所以 deque 作為 LIFO 隊列在多線程應用中是安全的,不需要加鎖。
- 如果有大量的包含檢測(if item in my_collection),使用集合更好,集合為包含檢測做了優化,保證極高的速度進行成員檢測,當然,需要注意的是集合是無序的。
(下一篇會介紹字典和集合常用操作及底層實現)
推薦閱讀:
※Mixin是什麼概念?
※Python數據分析及可視化實例之可視化圖表應用簡介
※win8環境下python3.4怎麼樣配置才能把scrapy安裝成功?
※python做音頻節奏識別(beat detector)有沒有現成的庫 (js也行)?
※python多進程進程間通信疑問,求大神指教?(主進程獲取不到子進程變數)