Python3 CookBook | 數據結構和演算法(一)
以下測試代碼全部基於 Python3。
Python 提供了大量的內置數據結構,包括列表,集合以及字典。在工作和編碼中,可以說天天和它們打交道,經常碰到查詢,排序和過濾等等這些問題,雖然每次解決這些問題並不困難,但總感覺代碼寫的很麻煩,不夠優雅。
最近通過閱讀《Python3 CookBook》,了解了一些更優秀的方法,做一些簡單記錄,與大家分享。
1、解壓可迭代對象賦值給多個變數
我們都知道,一個序列是可以賦值給多個變數的,就像下面這樣:
In [7]: p = (1, 2, 3)nnIn [8]: x, y, z = pnnIn [9]: xnOut[9]: 1n
但如果接收的變數個數和序列元素個數不一致,就會報錯,如果你不知道元素個數的話,可以採用下面這樣的方式:
In [10]: x, *y = pnnIn [11]: ynOut[11]: [2, 3]n
通過這種星號的方式,就可以解壓不確定個數或任意個數的可迭代對象了,是不是很棒呢?
那麼,用這個方法可以解決哪些問題呢?
先來看一種情況,現在有一個序列,去掉第一個數和最後一個數,然後求剩下數的平均值。
這個問題很簡單,我的第一反應是循環求和,然後計算平均值,顯然很麻煩。這時候星號表達式就派上用場了:
def drop_first_last(items):n first, *middle, last = itemsn return avg(middle)n
再看一種情況,比如字元串的分割:
In [12]: line = drwxr-xr-x 41 zyx staff 1.4K 11 24 08:53 zyxnnIn [13]: info, *fields, homedir = line.split( )nnIn [14]: infonOut[14]: drwxr-xr-xnnIn [15]: homedirnOut[15]: zyxn
2、保留最後 N 個元素
這個問題也是經常會遇到的,比如只取文件中滿足要求的前五行,或者只返回滿足要求的最新十條數據。我的第一反應是列表,然後通過 push 和 pop 來操作列表來實現。
其實通過 collections.deque 可以很容易解決這個問題,使用 deque(maxlen=N) 構造函數新建一個固定大小的隊列。當新元素加入並且這個隊列已滿時,最先進入隊列的元素便會被移除,符合先進先出的原則。
In [16]: from collections import dequennIn [17]: q = deque(maxlen=3)nnIn [18]: q.append(1)nnIn [19]: q.append(2)nnIn [20]: q.append(3)nnIn [21]: qnOut[21]: deque([1, 2, 3])nnIn [22]: q.append(4)nnIn [23]: qnOut[23]: deque([2, 3, 4])n
如果沒有設置 maxlen 則是一個無限大小的隊列,可以通過 appendleft 和 pop 在隊首和隊尾添加刪除元素。
3、字典中的鍵映射多個值
現在有一個需求,構建一個字典,key 是用戶 ID,value 為一個列表,列表元素可以是名字,電話等等,大概是這樣:
d = {id: [name, phone]}n
如果我們自己構建這個字典,可能會像下面這樣來實現:
d = {}nfor key, value in items:n if key not in d:n d[key] = valuen d[key].append(value)n
很麻煩,如果使用 collections 的 defaultdict 就很簡單了。defaultdict 的一個特徵就是它會自動初始化每個 key 剛開始對應的值,所以我們只關注添加元素操作就可以了。
優化後代碼就變成了這樣:
d = defaultdict(list)nfor key, value in items:n d[key].append(value)n
4、字典排序
字典是無序的,但如果要控制字典中元素的順序呢?可以使用 colletions 中的 OrderedDict,如下:
d = OrderedDict()nd[foo] = 1nd[bar] = 2nd[spam] = 3nd[grok] = 4n# Outputs "foo 1", "bar 2", "spam 3", "grok 4"nnfor key in d:n print(key, d[key])n
OrderedDict 內部維護這一個根據鍵插入順序排序的雙向鏈表。每次新元素插入時,便會被放在鏈表尾部,對於已經存在的鍵,並不會改變鍵的順序。
但需要注意的是,OrderedDict 的大小是普通字典的兩倍,所以在構建一個需要大量 OrderedDict 實例的數據結構時,就要考慮大量內存消耗的影響了。
5、字典的運算
如何取出字典中的最小值,或者對字典進行排序呢?
首先我們來看看直接使用普通的數學運算函數
In [25]: d = {a: 11, b: 43, c: 3, d: 65}nnIn [26]: min(d)nOut[26]: an
它比較的邏輯是直接比較 key,然後取出對應的 key,但如果要比較 value 呢?
In [28]: min(d.values())nOut[28]: 3n
結果是正確的,但似乎並不完美,如果鍵值一起返回就完美了。這時候就該 zip 登場了,它的作用是可以使鍵和值反轉過來。
In [29]: min(zip(d.values(), d.keys()))nOut[29]: (3, c)n
它直接返回了值最小的鍵和值,這樣就很好了,不管需要哪個信息都可以直接使用。如果要對這個字典排序的話也很簡單:
In [34]: sorted(zip(d.values(), d.keys()))nOut[34]: [(3, c), (11, a), (43, b), (65, d)]n
先寫這麼多吧,未完待續。。。
推薦閱讀: