2.37. 具名元組與雙端佇列¶
串列、元組、字典與集合涵蓋了大多數的資料需求。collections 模組中另有三種容器,適合內建型別處理起來笨拙的特定問題。
2.37.1. namedtuple——無需類別的具型別記錄¶
普通元組依位置儲存值。對於小型、短暫存活的 2 或 3 元組來說這沒問題,但超過這個範圍後,point[0] 與 point[1] 就開始無法說明它們究竟存了什麼。collections.namedtuple() 會回傳一個新的元組子類別,其欄位帶有名稱:
>>> from collections import namedtuple
>>> Reading = namedtuple('Reading', ('temp', 'humidity', 'ts'))
>>> r = Reading(22.5, 41.0, 137204)
>>> r.temp
22.5
>>> r.humidity
41.0
>>> r[0]
22.5
fields 引數是一個名稱字串的序列(或在 CPython 中為一個以空白分隔的字串;MicroPython 較為嚴格——請傳入元組或串列)。
為什麼要用 namedtuple 而不是類別?
它是一個元組。迭代、解包、相等性比較、雜湊,以及作為字典鍵使用,全都免費可用。
它是不可變的。重新指派
r.temp = ...會引發AttributeError,這正是你對記錄型別所期望的行為。它比具有相同欄位的類別實例耗用更少的 RAM——元組的儲存是連續的,沒有
__dict__。
與等效的類別相比,namedtuple 宣告只有一行。其取捨在於欄位是唯讀的——若要「更改」一筆讀值,你得建立一筆新的。
2.37.2. deque——有界的環形緩衝區¶
串列在「尾端」很快(append / pop),在「開頭」則很慢(insert(0, ...) / pop(0) 都會移動其他每一個元素)。collections.deque 在兩端都很快——它是一個由頭尾指標索引的環形緩衝區,因此無論 deque 持有多少項目,在任一端的 append 與 pop 都以相同的固定工作量執行。
在 MicroPython 中建構時,需要依序提供一個初始可迭代物件「以及」一個最大長度:
>>> from collections import deque
>>> events = deque((), 5)
>>> for i in range(8):
... events.append(i)
>>> list(events)
[3, 4, 5, 6, 7]
當有界的 deque 滿了時,每次 append 都會丟棄最舊的項目。這使得 deque 非常適合「最近 N 個樣本」、「最近 N 行日誌」,或任何不需要無限增長的滾動視窗。
MicroPython 的 deque 所公開的方法刻意維持精簡:
append(x)——加到右端。appendleft(x)——加到左端。extend(iterable)——將可迭代物件中的每個項目逐一附加。pop()——移除並回傳右端。在空時引發IndexError。popleft()——移除並回傳左端。
相較於 CPython 的 deque,值得注意的省略:沒有 clear、count、index、remove、reverse、rotate、maxlen 屬性或 __contains__。迭代與下標索引可以運作:
>>> events[0]
3
>>> for e in events:
... print(e)
一個典型的用途:保留最近幾筆感測器讀值以偵測趨勢變化:
history = deque((), 10)
def push(reading):
history.append(reading)
if len(history) == 10 and history[-1] > 2 * history[0]:
print('reading is climbing')
2.37.3. OrderedDict——當順序是相等性的一部分¶
自 MicroPython 1.13 與 CPython 3.7 起,一般的 dict 已保留插入順序。這涵蓋了人們過去之所以求助於 collections.OrderedDict 的最常見原因。
OrderedDict 仍能提供、而普通字典做不到的是:
OrderedDict的相等性會考量順序。兩個普通字典只要擁有相同的鍵/值對,無論插入順序為何都比較相等。兩個OrderedDict實例則只有在它們的配對順序相同時才相等:>>> from collections import OrderedDict >>> OrderedDict([('a', 1), ('b', 2)]) == OrderedDict([('b', 2), ('a', 1)]) False >>> {'a': 1, 'b': 2} == {'b': 2, 'a': 1} True
當你要把設定序列化成一個「確實」在意鍵順序的格式(TOML、某些 YAML 消費端)時,
OrderedDict很有用;或者當你想給程式碼的讀者一個清楚的文件提示、表明順序很重要時,它也很有用。
對於日常程式碼,請優先使用內建的 dict。只有在「順序是相等性的一部分」的語意,或文件上的價值確實帶來好處時,才求助於 OrderedDict。
這三種容器各有一個狹窄的適用情境。具名元組取代手工打造的記錄類別;deque 在有界佇列上取代串列;ordered dict 讓插入順序成為契約的一部分。