在內建資料型別(dict、list、set、tuple)的基礎上,collections模組提供了幾個額外的資料型別:

  • namedtuple:生成可以使用名字來訪問元素內容的tuple,通常用來增強程式碼的可讀性, 在訪問一些tuple型別的資料時尤其好用.
  • deque:雙端佇列,可以快速的從另外一側追加和推出物件.
  • Counter:計數器,主要用來計數.
  • OrderedDict:有序字典.
  • defaultdict:帶有預設值的字典.


namedtuple

首先,我們知道tuple可以表示不變集合,例如一個點的二維座標可以表示為:

>>> p = (1, 2)

可是,看到(1, 2),我們很難分辨出這個tuple是用來表示一個座標的.
這時,namedtuple就派上用場了:

>>> from collections import namedtuple
>>> Ponint = namedtuple('Ponint', ['x', 'y'])
>>> p = Ponint(1, 2)
>>> p.x
1
>>> p.y
2

再比如,有這樣一個數據結構:每一個物件都是擁有三個元素的tuple.
使用namedtuple方法就可以方便的通過tuple來生成可讀性更高也更好用的資料結構.

from collections import namedtuple

userinfo = [
    ("花千骨", "洪荒之力", "白子畫"),
    ("白淺", "玉清崑崙扇", "夜華"),
    ("錦覓", "翊聖玄冰", "旭鳳")
]

Userinfo = namedtuple('Userinfo', ["姓名", "必殺技", "男朋友"])
[print(Userinfo._make(ui)) for ui in userinfo]

"""列印結果如下:
Userinfo(姓名='花千骨', 必殺技='洪荒之力', 男朋友='白子畫')
Userinfo(姓名='白淺', 必殺技='玉清崑崙扇', 男朋友='夜華')
Userinfo(姓名='錦覓', 必殺技='翊聖玄冰', 男朋友='旭鳳')
"""

deque

所謂的單端佇列,就是一端進,另一端出.
所謂的雙端佇列,就是兩端都可以進出.

deque其實是double-ended queue的縮寫,翻譯過來就是雙端佇列.
deque最大的好處就是實現了從佇列頭部快速增加和取出物件:.popleft(), .appendleft()

你可能會說,原生的list也可以從頭部新增和取出物件啊?就像這樣:

lst.insert(0, 'a')
lst.pop(0)

但是,我們得注意了,list物件的這兩種用法的時間複雜度為O(n),也就是說隨著元素數量的增加,耗時呈線性上升.
而使用deque物件則是O(1)的複雜度,所以當你的程式碼有這樣的需求的時候,一定要記得使用deque.

deque與list的效率問題
·

  • 使用list儲存資料時,按索引訪問元素很快,但是插入和刪除元素就很慢了,因為list是線性儲存,資料量大的時候,插入和刪除的效率會很低.
  • 在使用insert、remove方法時,deque的平均效率要遠高於list.
  • list根據索引查某個值的效率要高於deque.
  • append和pop方法對於列表的效率沒有影響.

示例 deque兩端進出

>>> from collections import deque
>>> dq = deque([1, 2, 3])
>>> dq.append('z')
>>> dq.appendleft('a')  # 從頭部新增元素
>>> dq.pop()
'z'
>>> dq.popleft()  # 從頭部彈出元素
'a'

作為一個雙端佇列,deque還提供了其它的好用的方法,比如rotate.
下面是一個有趣的例子,主要使用了deque的rotate方法來實現一個無限迴圈的載入動畫.

from collections import deque
import sys
import time

running = deque('>---------------------')
while 1:
    print('\r', ''.join(running), end='')
    running.rotate(1)
    sys.stdout.flush()
    time.sleep(0.2)

# 一個無限迴圈的跑馬燈
# ------------>--------

Counter

Counter類的目的是用來跟蹤值出現的次數.
Counter是一個無序的容器型別,以字典的鍵值對形式儲存,其中元素作為key,計數作為value,計數值可以是任意的Interger(包括0和負數).
Counter類和其它語言的bags或multisets很相似.

示例 使用Counter模組統計一段句子裡面所有字元出現次數

from collections import Counter

s = '''
    A Counter is a dict subclass for counting hashable objects. 
    It is an unordered collection where elements are stored as dictionary keys and 
    their counts are stored as dictionary values. 
    Counts are allowed to be any integer value including zero or negative counts. 
    The Counter class is similar to bags or multisets in other languages.
'''.lower()

c = Counter(s)
# 獲取出現頻率最高的4個字元
print(c.most_common(4))  # [(' ', 74), ('e', 32), ('s', 25), ('a', 24)]

OrderedDict

在Python中,dict這個資料結構由於hash的特性,是無序的,這在有的時候會給我們帶來一些麻煩.
幸運的是,collections模組為我們提供了OrderedDict,當你要獲得一個有序的字典物件時,用它就對了.

Python3.6版本以後的字典是有序的了.

簡單示例

>>> from collections import OrderedDict
>>> d = dict([('a', 1), ('b', 2), ('c', 3)])
>>> d # dict的Key是無序的
{'a': 1, 'c': 3, 'b': 2}
>>> od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
>>> od # OrderedDict的Key是有序的
OrderedDict([('a', 1), ('b', 2), ('c', 3)])

·
但要注意的是,OrderedDict的key是按照插入的順序排列的,而不是Key本身排序:

>>> od = OrderedDict()
>>> od['z'] = 1
>>> od['y'] = 2
>>> od['x'] = 3
>>> od.keys() # 按照插入的Key的順序返回
['z', 'y', 'x']

defaultdict

一個示例
·
有這麼一個集合:[1, 2, 3, 4, 5, 7, 8, 9]
將所有小於5的值儲存至字典的第一個key中,大於5的值儲存至第二個key中.
·
我們先來看看dict如何實現:

lst = [1, 2, 3, 4, 5, 7, 8, 9]
dct = {}
for i in lst:
   key = 'k1' if i < 5 else 'k2'
    dct.setdefault(key, []).append(i)

再來看看defaultdict如何實現:

from collections import defaultdict

lst = [1, 2, 3, 4, 5, 7, 8, 9]
dd = defaultdict(list)

for i in lst:
    key = 'k1' if i < 5 else 'k2'
    dd[key].append(i)

# defaultdict(<class 'list'>, {'k1': [1, 2, 3, 4], 'k2': [5, 7, 8, 9]})

還有,在使用Python原生的資料結構dict的時候,如果用d[key]這樣的方式訪問, 當指定的key不存在時,是會丟擲KeyError異常的.

但是,如果使用defaultdict,只要你傳入一個預設的工廠方法,那麼請求一個不存在的key時, 便會呼叫這個工廠方法使用其結果來作為這個key的預設值.

>>> from collections import defaultdict
>>> dd = defaultdict(lambda: 'N/A')
>>> dd['name'] = "花千骨"
>>> dd['name']
'花千骨'
>>> dd['sex']   #'sex'不存在,將返回預設值
'N/A'

上面只是非常簡單的介紹了一下collections模組的主要內容,主要目的就是當你碰到適合使用它們的場所時,能夠記起並使用它們,起到事半功倍的效果.

如果想要對它們有一個更全面和深入瞭解的話,建議閱讀官方文件和模組原始碼,Go.