本文始發於我的公衆號:TechFlow,原創不易,求個關注python
今天爲你們介紹Python當中一個很好用也是很基礎的工具庫,叫作collections。算法
collection在英文當中有容器的意思,因此顧名思義,這是一個容器的集合。這個庫當中的容器不少,有一些不是很經常使用,本篇文章選擇了其中最經常使用的幾個,一塊兒介紹給你們。編程
defaultdict能夠說是這個庫當中使用最簡單的一個,而且它的定義也很簡單,咱們從名稱基本上就能看得出來。它解決的是咱們使用dict當中最多見的問題,就是key爲空的狀況。api
在正常狀況下,咱們在dict中獲取元素的時候,都須要考慮key爲空的狀況。若是不考慮這點,那麼當咱們獲取了一個不存在的key,會致使系統拋出異常。咱們固然能夠在每次get以前寫一個if判斷,可是這很麻煩,好比:安全
if key in dict:
return dict[key]
else:
return None
複製代碼
固然,這是最笨的方法,dict當中爲咱們提供了帶默認值的get方法。好比,咱們能夠寫成:數據結構
return dict.get(key, None)
複製代碼
這樣,當key不在dict當中存在的時候,會自動返回咱們設置的默認值。這個省去了不少麻煩的判斷,可是在一些特殊狀況下仍然存在一點問題。舉個例子,好比當key存在重複,咱們但願將key相同的value存進一個list當中,而不是隻保留一個。這種狀況下寫成代碼就會比較複雜:併發
data = [(1, 3), (2, 1), (1, 4), (2, 5), (3, 7)]
d = {}
for k, v in data:
if k in d:
d[k].append(v)
else:
d[k] = [v]
複製代碼
因爲dict的value是一個list,因此咱們仍是須要判斷是否爲空,不能直接使用默認值,間接操做固然能夠,可是仍是不夠簡單:app
for k, v in data:
cur = d.get(k, [])
cur.append(v)
d[k] = v
複製代碼
這和使用if區別並不大,爲了完美解決這個問題,咱們可使用collections當中的defaultdict:函數
from collections import defaultdict
d = defaultdict(list)
for k, v in data:
d[k].append(v)
複製代碼
使用defaultdict以後,若是key不存在,容器會自動返回咱們預先設置的默認值。須要注意的是defaultdict傳入的默認值能夠是一個類型也能夠是一個方法。若是咱們傳入int,那麼默認值會被設置成int()的結果,也就是0,若是咱們想要自定義或者修改,咱們能夠傳入一個方法,好比:工具
d = defaultdict(lambda: 3)
for k, v in data:
d[k] += v
複製代碼
這是一個很是經常使用和很是強大的工具,咱們常常用到。
在咱們實際的編程當中,咱們常常遇到一個問題,就是數數和排序。好比說咱們在分析文本的時候,會獲得一堆單詞。其中可能有大量的長尾詞,在整個文本當中可能只出現過寥寥幾回。因而咱們但願計算一下這些單詞出現過的數量,只保留出現次數最高的若干個。
這個需求讓咱們本身實現固然也不困難,咱們徹底能夠建立一個dict,而後對這些單詞一個一個遍歷。本來咱們還須要考慮單詞以前沒有出現過的狀況,若是咱們上面說的defaultdict,又要簡單許多。可是咱們仍是少不了計數而後排序的步驟,若是使用Counter這個步驟會縮減成一行代碼。
舉個例子:
words = ['apple', 'apple', 'pear', 'watermelon', 'pear', 'peach']
from collections import Counter
counter = Counter(words)
>>> print(counter)
Counter({'apple': 2, 'pear': 2, 'watermelon': 1, 'peach': 1})
複製代碼
咱們直接將一個list傳入Counter中做爲參數,它會自動爲咱們替當中的每一個元素計數。
若是咱們要篩選topK,也很是簡單,它爲咱們提供了most_common方法,咱們只須要傳入須要求的K便可:
counter.most_common(1)
[('apple', 2)]
複製代碼
除此以外,它的構造函數還接收dict類型。咱們能夠直接經過一個value是int類型的dict來初始化一個Counter,好比:
c = Counter({'apple': 5, 'pear': 4})
c = Counter(apple=4, pear=3)
複製代碼
而且,它還支持加減法的操做,好比咱們能夠將兩個Counter相加,它會自動將兩個Counter合併,相同的key對應的value累加。相減也是同理,會將能對應的value作減法,被減的key對應不上的會保留,而減數中對應不上的key則會被丟棄。而且須要注意,Counter支持value爲負數。
咱們都知道queue是隊列,deque也是隊列,不過稍稍特殊一些,是雙端隊列。對於queue來講,只容許在隊尾插入元素,在隊首彈出元素。而deque既然稱爲雙端隊列,那麼說明它的隊首和隊尾都支持元素的插入和彈出。相比於普通的隊列,要更加靈活一些。
除了經常使用的clear、copy、count、extend等api以外,deque當中最經常使用也是最核心的api還有append、pop、appendleft和popleft。從名字上咱們就看得出來,append和pop和list的append和pop同樣,而appendleft和popleft則是在隊列左側,也就是頭部進行pop和append的操做。很是容易理解。
在平常的使用當中,真正用到雙端隊列的算法其實不太多。大多數狀況下咱們使用deque主要有兩個緣由,第一個緣由是deque收到GIL的管理,它是線程安全的。而list則沒有GIL鎖,所以不是線程安全的。也就是說在併發場景下,list可能會致使一致性問題,而deque不會。另外一個緣由是deque支持固定長度,當長度滿了以後,當咱們繼續append時,它會自動彈出最先插入的數據。
好比說當咱們擁有海量的數據,咱們不知道它的數量,可是想要保留最後出現的指定數量的數據的時候,就可使用deque。
from collections import deque
dque = deque(maxlen=10)
# 假設咱們想要從文件當中獲取最後10條數據
for i in f.read():
dque.append(i)
複製代碼
namedtuple很特殊,它涉及到元編程的概念。簡單介紹一下元編程的概念,咱們不作過多的深刻。簡而言之,就是在常見的面向對象當中。咱們都是定義類,而後經過類的構造函數來建立實例。而元編程指的是咱們定義元類,根據元類建立出來的並非一個實例,而是一個類。若是用模具和成品來分別比喻類和實例的話,元類至關因而模具的模具。
namedtuple是一個很是簡單的元類,經過它咱們能夠很是方便地定義咱們想要的類。
它的用法很簡單,咱們直接來看例子。好比若是咱們想要定義一個學生類,這個類當中有name、score、age這三個字段,那麼這個類會寫成:
class Student:
def __init__(self, name=None, score=None, age=None):
self.name = name
self.score = score
self.age = age
複製代碼
這還只是粗略的寫法,若是考慮規範,還須要定義property等註解,又須要不少代碼。若是咱們使用namedtuple能夠簡化這個工做,咱們來看代碼:
from collections import namedtuple
# 這個是類,columns也能夠寫成'name score age',即用空格分開
Student = namedtuple('Student', ['name', 'score', 'age'])
# 這個是實例
student = Student(name='xiaoming', score=99, age=10)
print(student.name)
複製代碼
經過使用namedtuple,咱們只須要一行就定義了一個類,可是這樣定義的類是沒有缺失值的,可是namedtuple很強大,咱們能夠經過傳入defaults參數來定義缺失值。
Student = namedtuple('Student', ['name', 'score', 'age'], defaults=(0, 0))
複製代碼
能夠注意到,雖然咱們定義了三個字段,可是咱們只設置了兩個缺失值。在這種狀況下,namedtuple會自動將缺失值匹配上score和age兩個字段。由於在Python的規範當中,必選參數必定在可選參數前面。因此nuamdtuple會自動右對齊。
細數一下,咱們今天的文章當中介紹了defaultdict、Counter、deque和namedtuple這四種數據結構的用法。除了這四個以外,collections庫當中還有一些其餘的工具類,只是咱們用的頻率稍稍低一些,加上因爲篇幅的緣由,這裏就很少作贅述了。感興趣的同窗能夠自行查看相關的api和文檔。
今天的文章就是這些,若是以爲有所收穫,請順手掃碼點個關注吧,大家的舉手之勞對我來講很重要。