collections --- 容器資料型別

原始碼:Lib/collections/__init__.py


這個模組實作了一些特別的容器資料型別,用來替代 Python 一般內建的容器,例如 dict(字典)、list(串列)、set(集合)和 tuple(元組)。

namedtuple()

用來建立具名欄位的 tuple 子類別的工廠函式

deque

一個類似 list 的容器,可以快速的在頭尾加入 (append) 元素與移除 (pop) 元素

ChainMap

一個類似 dict 的類別,用來為多個對映 (mapping) 建立單一的視圖 (view)

Counter

dict 的子類別,用來計算可雜湊物件的數量

OrderedDict

dict 的子類別,會記錄物件被加入的順序

defaultdict

dict 的子類別,當值不存在 dict 中時會呼叫一個提供預設值的工廠函式

UserDict

dict 物件的包裝器 (wrapper),簡化了 dict 的子類別化過程

UserList

list 物件的包裝器,簡化了 list 的子類別化過程

UserString

字串物件的包裝器,簡化了字串的子類別化過程

ChainMap 物件

在 3.3 版被加入.

ChainMap(對映鏈結)類別的目的是快速將數個對映連結在一起,讓它們可以被當作一個單元來處理。它通常會比建立一個新的字典並多次呼叫 update() 來得更快。

這個類別可用於模擬巢狀作用域 (nested scopes),且在模板化 (templating) 時能派上用場。

class collections.ChainMap(*maps)

一個 ChainMap 將多個 dict 或其他對映組合在一起,建立一個獨立、可更新的視圖。如果沒有指定 maps,預設會提供一個空字典讓每個新鏈結都至少有一個對映。

底層的對映儲存於一個 list 中,這個 list 是公開的且可透過 maps 屬性存取或更新,沒有其他狀態 (state)。

檢索 (lookup) 陸續查詢底層對映,直到鍵被找到,然而讀取、更新和刪除就只會對第一個對映操作。

ChainMap 透過參照將底層對映合併,所以當一個底層對映被更新,這些改變也會反映到 ChainMap

所有常見的字典方法都有支援。此外,還有一個 maps 屬性 (attribute)、一個建立子上下文 (subcontext) 的方法、和一個能夠存取除了第一個以外其他所有對映的特性 (property):

maps

一個可被更新的對映列表,這個列表是按照被搜尋的順序來排列,在 ChainMap 中它是唯一被儲存的狀態,可被修改來變換搜尋順序。回傳的列表都至少包含一個對映。

new_child(m=None, **kwargs)

回傳包含一個新對映的 ChainMap, 新對映後面接著目前實例的所有現存對映。如果有給定 mm 會成為那個最前面的新對映;若沒有指定,則會加上一個空 dict,如此一來呼叫 d.new_child() 就等同於 ChainMap({}, *d.maps)。這個方法用於建立子上下文,而保持父對映的不變。

在 3.4 版的變更: 加入可選參數 m

在 3.10 版的變更: 增加了對關鍵字引數的支援。

parents

回傳一個包含除了第一個以外所有其他對映的新 ChainMap 的特性,可用於需要跳過第一個對映的搜尋。使用情境類似於在巢狀作用域當中使用 nonlocal 關鍵字,也可與內建函式 super() 做類比。引用 d.parents 等同於 ChainMap(*d.maps[1:])

注意,一個 ChainMap 的疊代順序是透過由後往前掃描對映而定:

>>> baseline = {'music': 'bach', 'art': 'rembrandt'}
>>> adjustments = {'art': 'van gogh', 'opera': 'carmen'}
>>> list(ChainMap(adjustments, baseline))
['music', 'art', 'opera']

這和呼叫 dict.update() 結果的順序一樣是從最後一個對映開始:

>>> combined = baseline.copy()
>>> combined.update(adjustments)
>>> list(combined)
['music', 'art', 'opera']

在 3.9 版的變更: 支援 ||= 運算子,詳見 PEP 584

也參考

ChainMap 範例和用法

此章節提供了多種操作 ChainMap 的案例。

模擬 Python 內部檢索鏈結的例子:

import builtins
pylookup = ChainMap(locals(), globals(), vars(builtins))

讓使用者指定的命令列引數優先於環境變數、再優先於預設值的範例:

import os, argparse

defaults = {'color': 'red', 'user': 'guest'}

parser = argparse.ArgumentParser()
parser.add_argument('-u', '--user')
parser.add_argument('-c', '--color')
namespace = parser.parse_args()
command_line_args = {k: v for k, v in vars(namespace).items() if v is not None}

combined = ChainMap(command_line_args, os.environ, defaults)
print(combined['color'])
print(combined['user'])

ChainMap 類別模擬巢狀上下文的範例模式:

c = ChainMap()        # Create root context
d = c.new_child()     # Create nested child context
e = c.new_child()     # Child of c, independent from d
e.maps[0]             # Current context dictionary -- like Python's locals()
e.maps[-1]            # Root context -- like Python's globals()
e.parents             # Enclosing context chain -- like Python's nonlocals

d['x'] = 1            # Set value in current context
d['x']                # Get first key in the chain of contexts
del d['x']            # Delete from current context
list(d)               # All nested values
k in d                # Check all nested values
len(d)                # Number of nested values
d.items()             # All nested items
dict(d)               # Flatten into a regular dictionary

ChainMap 類別只對鏈結中第一個對映來做寫入或刪除,但檢索則會掃過整個鏈結。但如果需要對更深層的鍵寫入或刪除,透過定義一個子類別來實作也不困難:

class DeepChainMap(ChainMap):
    'Variant of ChainMap that allows direct updates to inner scopes'

    def __setitem__(self, key, value):
        for mapping in self.maps:
            if key in mapping:
                mapping[key] = value
                return
        self.maps[0][key] = value

    def __delitem__(self, key):
        for mapping in self.maps:
            if key in mapping:
                del mapping[key]
                return
        raise KeyError(key)

>>> d = DeepChainMap({'zebra': 'black'}, {'elephant': 'blue'}, {'lion': 'yellow'})
>>> d['lion'] = 'orange'         # update an existing key two levels down
>>> d['snake'] = 'red'           # new keys get added to the topmost dict
>>> del d['elephant']            # remove an existing key one level down
>>> d                            # display result
DeepChainMap({'zebra': 'black', 'snake': 'red'}, {}, {'lion': 'orange'})

Counter 物件

提供一個支援方便且快速計數的計數器工具,例如:

>>> # Tally occurrences of words in a list
>>> cnt = Counter()
>>> for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']:
...     cnt[word] += 1
...
>>> cnt
Counter({'blue': 3, 'red': 2, 'green': 1})

>>> # Find the ten most common words in Hamlet
>>> import re
>>> words = re.findall(r'\w+', open('hamlet.txt').read().lower())
>>> Counter(words).most_common(10)
[('the', 1143), ('and', 966), ('to', 762), ('of', 669), ('i', 631),
 ('you', 554),  ('a', 546), ('my', 514), ('hamlet', 471), ('in', 451)]
class collections.Counter(**kwargs)
class collections.Counter(iterable, /, **kwargs)
class collections.Counter(mapping, /, **kwargs)

Counterdict 的子類別,用來計算可雜湊物件的數量。它是將物件與其計數作為字典的鍵值對儲存的集合容器。計數可以是包含 0 與負數的任何整數值。Counter 類別類似其他程式語言中的 bags 或 multisets。

被計數的元素來自一個 iterable 或是被其他的 mapping(或 Counter)初始化:

>>> c = Counter()                           # a new, empty counter
>>> c = Counter('gallahad')                 # a new counter from an iterable
>>> c = Counter({'red': 4, 'blue': 2})      # a new counter from a mapping
>>> c = Counter(cats=4, dogs=8)             # a new counter from keyword args

Counter 物件擁有一個字典的使用介面,除了遇到 Counter 中沒有的值時會回傳計數 0 取代 KeyError 這點不同:

>>> c = Counter(['eggs', 'ham'])
>>> c['bacon']                              # count of a missing element is zero
0

將一個值的計數設為 0 並不會真的從 Counter 中刪除這個元素,要使用 del 來將其刪除:

>>> c['sausage'] = 0                        # counter entry with a zero count
>>> del c['sausage']                        # del actually removes the entry

在 3.1 版被加入.

在 3.7 版的變更: 作為 dict 的子類別,Counter 繼承了記憶插入順序的功能。對 Counter 做數學運算後同樣保留順序性,其結果是依照各個元素在運算元左邊出現的時間先後、再按照運算元右邊出現的時間先後來排列。

除了字典原本就有的方法外,Counter 物件額外支援數個新方法:

elements()

回傳每個元素都重複出現計算次數的 iterator(疊代器)物件,其中元素的回傳順序是依照各元素首次出現的時間先後。如果元素的出現次數小於 1,elements() 方法會忽略這些元素。

>>> c = Counter(a=4, b=2, c=0, d=-2)
>>> sorted(c.elements())
['a', 'a', 'a', 'a', 'b', 'b']
most_common(n=None)

回傳一個 list,包含出現最多次的 n 個元素及其出現次數,並按照出現次數排序。如果 n 被省略或者為 Nonemost_common() 會回傳所有 counter 中的元素。出現次數相同的元素會按照首次出現的時間先後來排列:

>>> Counter('abracadabra').most_common(3)
[('a', 5), ('b', 2), ('r', 2)]
subtract(**kwargs)
subtract(iterable, /, **kwargs)
subtract(mapping, /, **kwargs)

減去自一個 iterable 或另一個對映(或 Counter)中的計數元素,行為類似 dict.update() 但是是為了減去計數而非取代其值。輸入和輸出都可以是 0 或是負數。

>>> c = Counter(a=4, b=2, c=0, d=-2)
>>> d = Counter(a=1, b=2, c=3, d=4)
>>> c.subtract(d)
>>> c
Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})

在 3.2 版被加入.

total()

計算總計數值。

>>> c = Counter(a=10, b=5, c=0)
>>> c.total()
15

在 3.10 版被加入.

通常來說字典方法也可以用於 Counter 物件,除了以下兩個作用方式與計數器不同。

fromkeys(iterable)

此類別方法沒有被實作於 Counter 物件中。

update(**kwargs)
update(iterable, /, **kwargs)
update(mapping, /, **kwargs)

加上自一個 iterable 計算出的計數或加上另一個 mapping(或 Counter)中的計數,行為類似 dict.update() 但是是為了加上計數而非取代其值。另外,iterable 需要是一串將被計算個數元素的序列,而非元素為 (key, value) 形式的序列。

Counter 支援相等性、子集和超集關係的 rich comparison 運算子:==!=<<=>>=。這些檢測會將不存在的元素之計數值當作零,因此 Counter(a=1) == Counter(a=1, b=0) 將回傳真值。

在 3.10 版的變更: 增加了 rich comparison 運算。