Python collections 模塊筆記

namedtuple

collections.namedtuple 是一個工廠函數,它能夠用來構建一個帶字段名的元組和一個有名字的類——這個帶名字的類對調試程序有很大幫助。php

咱們能夠這樣建立一個 User 類:html

Card = collections.namedtuple('User', ['name', 'age', 'height'])
複製代碼

如何用具名元組來記錄一個城市的信息java

In [1]: from collections import namedtuple

In [2]: City = namedtuple('City', 'name country population coordinates')

In [3]: tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667))

In [4]: tokyo
Out[4]: City(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722, 139.691667))

In [5]: tokyo.population
Out[5]: 36.933

In [6]: tokyo.coordinates
Out[6]: (35.689722, 139.691667)

In [7]: tokyo[1]
Out[7]: 'JP'
複製代碼

建立一個具名元組須要兩個參數,一個是類名,另外一個是類的各個字段的名字。後者能夠是由數個字符串組成的可迭代對象,或者是由空格分隔開的字段名組成的字符串。python

除了從普通元組那裏繼承來的屬性以外,具名元組還有一些本身專有的屬性。c++

In [8]: City._fields
Out[8]: ('name', 'country', 'population', 'coordinates')

In [9]: LatLong = namedtuple('LatLong', 'lat long')

In [10]: delhi_data = ('Delhi NCR', 'IN', 21.935, LatLong(28.613889, 77.208889))

In [11]: delhi = City._make(delhi_data)

In [12]: delhi._asdict()
Out[12]: 
OrderedDict([('name', 'Delhi NCR'),
             ('country', 'IN'),
             ('population', 21.935),
             ('coordinates', LatLong(lat=28.613889, long=77.208889))])

In [13]: for key, value in delhi._asdict().items():
    ...:     print(key + ':', value)
    ...:     
name: Delhi NCR
country: IN
population: 21.935
coordinates: LatLong(lat=28.613889, long=77.208889)

複製代碼

_fields 屬性是一個包含這個類全部字段名稱的元組。swift

_make() 經過接受一個可迭代對象來生成這個類的一個實例,它的做用跟 City(*delhi_data) 是同樣的。安全

_asdict() 把具名元組以 collections.OrderedDict 的形式返回,咱們能夠利用它來把元組裏的信息友好地呈現出來。app

defaultdict

首先咱們看一個例子。函數

用 dict 統計一個 list 中字符串出現的次數:spa

In [1]: langs = ['java', 'php', 'python', 'C#', 'kotlin', 'swift', 'python']

In [2]: res_dict = {}

In [3]: for lang in langs:
   ...:     if lang in res_dict:
   ...:         res_dict[lang] += 1
   ...:     else:
   ...:         res_dict[lang] = 1
   ...: 

In [4]: res_dict
Out[4]: {'C#': 1, 'java': 1, 'kotlin': 1, 'php': 1, 'python': 2, 'swift': 1}

複製代碼

這裏每次循環都要判斷一次,能夠調用 setdefault 方法來消除判斷。

In [1]: langs = ['java', 'php', 'python', 'C#', 'kotlin', 'swift', 'python']

In [2]: res_dict = {}

In [3]: for lang in langs:
   ...:     res_dict.setdefault(lang, 0)
   ...:     res_dict[lang] += 1
   ...: 

In [4]: res_dict
Out[4]: {'C#': 1, 'java': 1, 'kotlin': 1, 'php': 1, 'python': 2, 'swift': 1}
複製代碼

可是如今還有一個錯誤,每次取值使還要進行一次判斷,不然若是值不存在就會拋異常:

In [5]: res_dict['c++']
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-5-269671e9ed5a> in <module>()
----> 1 res_dict['c++']

KeyError: 'c++'

複製代碼

有時候爲了方便起見,就算某個鍵在映射裏不存在,咱們也但願在經過這個鍵讀取值的時候能獲得一個默認值。有兩個途徑能幫咱們達到這個目的,一個是經過 defaultdict 這個類型而不是普通的 dict,另外一個是給本身定義一個 dict 的子類,而後在子類中實現 __missing__ 方法。

使用 defaultdict

In [7]: from collections import defaultdict

In [8]: res_dict= defaultdict(int)

In [9]: for lang in langs:
   ...:     res_dict[lang] += 1
   ...: 

In [10]: res_dict
Out[10]: 
defaultdict(int,
            {'C#': 1,
             'java': 1,
             'kotlin': 1,
             'php': 1,
             'python': 2,
             'swift': 1})

In [11]: res_dict['c++']
Out[11]: 0
複製代碼

這樣就完美解決了上述全部問題, defaultdict 構造函數接收一個可調用的對象,當 __getitem__ 方法找不到值的時候就會調用該對象返回一個值。

因此咱們能夠返回更復雜的默認值:

In [25]: def gen_dict():
    ...:     return {'name': 'None', 'age': 0}
    ...: 

In [26]: res_dict = defaultdict(gen_dict)

In [27]: res_dict['zhangsan']
Out[27]: {'age': 0, 'name': 'None'}

複製代碼

__missing__ 方法

In [28]: class CustomDict(dict):
    ...:     
    ...:     def __missing__(self, key):
    ...:         return {'name': 'None', 'age': 18}
    ...: 

In [29]: res_dict = CustomDict()

In [30]: res_dict['lisi']
Out[30]: {'age': 18, 'name': 'None'}

複製代碼

deque

collections.deque 類(雙向隊列)是一個線程安全、能夠快速從兩端添加或者刪除元素的數據類型。並且若是想要有一種數據類型來存放「最近用到的幾個元素」,deque 也是一個很好的選擇。這是由於在新建一個雙向隊列的時候,你能夠指定這個隊列的大小,若是這個隊列滿員了,還能夠從反向端刪除過時的元素,而後在尾端添加新的元素。

In [1]: from collections import deque

In [2]: dq = deque(range(10), maxlen=10)

In [3]: dq
Out[3]: deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [4]: dq.rotate(3)

In [5]: dq
Out[5]: deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6])

In [6]: dq.rotate(-4)

In [7]: dq
Out[7]: deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0])

In [8]: dq.appendleft(-1)

In [9]: dq
Out[9]: deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [10]: dq.extend([11, 22, 33])

In [11]: dq
Out[11]: deque([3, 4, 5, 6, 7, 8, 9, 11, 22, 33])

In [12]: dq.extendleft([10, 20, 30, 40])

In [13]: dq
Out[13]: deque([40, 30, 20, 10, 3, 4, 5, 6, 7, 8])

複製代碼

maxlen 是一個可選參數,表明這個隊列能夠容納的元素的數量,並且一旦設定,這個屬性就不能修改了。

隊列的旋轉操做 (rotate) 接受一個參數 n,當 n > 0 時,隊列的最右邊的 n 個元素會被移動到隊列的左邊。當 n < 0 時,最左邊的 n 個元素會被移動到右邊。

當試圖對一個已滿(len(d) == d.maxlen)的隊列作尾部添加操做的時候,它頭部的元素會被刪除掉。

extendleft(iter) 方法會把迭代器裏的元素逐個添加到雙向隊列的左邊,所以迭代器裏的元素會逆序出如今隊列裏。

Counter

這個映射類型會給鍵準備一個整數計數器。每次更新一個鍵的時候都會增長這個計數器。因此這個類型能夠用來給可散列表對象計數,或者是當成多重集來用——多重集合就是集合裏的元素能夠出現不止一次。Counter 實現了 + 和 - 運算符用來合併記錄,還有像 most_common([n]) 這類頗有用的方法。most_common([n]) 會按照次序返回映射裏最多見的 n 個鍵和它們的計數

In [1]: from collections import Counter

In [2]: langs = ['java', 'php', 'python', 'C#', 'kotlin', 'swift', 'python']

In [3]: ct = Counter(langs)

In [4]: ct
Out[4]: Counter({'C#': 1, 'java': 1, 'kotlin': 1, 'php': 1, 'python': 2, 'swift': 1})

In [5]: ct.update(['java', 'c'])

In [6]: ct
Out[6]: 
Counter({'C#': 1,
         'c': 1,
         'java': 2,
         'kotlin': 1,
         'php': 1,
         'python': 2,
         'swift': 1})

In [7]: ct.most_common(2)
Out[7]: [('java', 2), ('python', 2)]
複製代碼

固然,也能夠直接操做字符串:

In [9]: ct = Counter('abracadabra')

In [10]: ct
Out[10]: Counter({'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2})

In [11]: ct.update('aaaaazzz')

In [12]: ct
Out[12]: Counter({'a': 10, 'b': 2, 'c': 1, 'd': 1, 'r': 2, 'z': 3})

In [13]: ct.most_common(2)
Out[13]: [('a', 10), ('z', 3)]

複製代碼

OrderedDict

這個類型在添加鍵的時候會保持順序,所以鍵的迭代次序老是一致的。OrderedDict 的 popitem 方法默認刪除並返回的是字典裏的最後一個元素,可是若是像 my_odict.popitem(last=False) 這樣調用它,那麼它刪除並返回第一個被添加進去的元素。

move_to_end(key, last=True) 將現有 key 移至有序字典的末尾。若是 last=True(默認),則 item 移動到右側,若是 last=False,則移動到開始。若是 key 不存在,則引起 KeyError

In [1]: from collections import OrderedDict

In [2]: d = OrderedDict.fromkeys('abcde')

In [3]: d.move_to_end('b')

In [4]: ''.join(d.keys())
Out[4]: 'acdeb'

In [5]: d.move_to_end('b', last=False)

In [6]: ''.join(d.keys())
Out[6]: 'bacde'

複製代碼

因爲 OrderedDict 會記住它的插入順序,所以它能夠與 sorted 結合使用來建立一個排序後的字典:

In [11]: d = {'banana': 3, 'apple': 4, 'pear': 1, 'orange': 2}
# 根據 key 排序
In [12]: OrderedDict(sorted(d.items(), key=lambda t:t[0]))
Out[12]: OrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)])
# 根據 value 排序
In [13]: OrderedDict(sorted(d.items(), key=lambda t:t[1]))
Out[13]: OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])
# 根據 key 的長度排序
In [14]: OrderedDict(sorted(d.items(), key=lambda t: len(t[0])))
Out[14]: OrderedDict([('pear', 1), ('apple', 4), ('banana', 3), ('orange', 2)])

複製代碼

刪除條目時,新排序的字典會保持排序順序。可是,當添加新的 key 時,key 被追加到最後,並不保持排序。

ChainMap

ChainMap 類提供用於快速連接多個 dict,以便將它們視爲單個單元。它一般比建立新 dict 和運行多個 update() 調用要快得多。

In [1]: from collections import ChainMap

In [2]: d1 = {'java': 3, 'python': 4}

In [3]: d2 = {'c++': 1, 'java': 2}

In [4]: for key, val in ChainMap(d1, d2).items():
   ...:     print(key, val)
   ...:     
c++ 1
java 3
python 4

複製代碼

後出現的重複的 key 將被忽略

ChainMap 將連接的 dict 存儲在一個列表中。該列表是公開的,可使用 maps 屬性進行訪問或更新。

In [10]: c1 = ChainMap(d1, d2)

In [11]: c1.maps[0]
Out[11]: {'java': 3, 'python': 4}

In [12]: c1.maps[0]['python'] = 2

In [13]: c1.items()
Out[13]: ItemsView(ChainMap({'java': 3, 'python': 2}, {'c++': 1, 'java': 2}))

In [14]: dict(c1)
Out[14]: {'c++': 1, 'java': 3, 'python': 2}

複製代碼

參考

python必學模塊-collections
8.3. collections — Container datatypes 《流暢的 Python》相關章節

相關文章
相關標籤/搜索