
引言
我們在【盤一盤 Python 下】一貼介紹過 5 種類型的容器型(container)資料,分别是字元串(string)、清單(list)、元組(tuple)、字典(dictionary)和集合(set)。
顧名思義,容器型就是将一組元素打包在一起。有人覺得字元串不是容器型資料,但我認為它将一個個字元(character)打包在一起,是以也算是容器型資料。
今天我們來介紹除上面 5 個之外的容器型資料,統稱 Collection,見下圖。
上圖中的 UserDict, UserList 和 UserString 隻是字典、清單和字元串的一個簡單封裝,而 frozenset 就是“元素不可修改”的集合,它們平時使用的頻率很少,是以略過不講。
本帖隻介紹 6 種使用較多(和上面 4 種較少的相比而言)的 Collection,它們是
- namedtuple
- defaultdict
- Counter
- deque
- ChainMap
- OrderedDict
再具體講它們之前,我們來思考一個基礎問題,為什麼要創造它們?字元串清單元組字典集合這“五大金剛”不香嗎?創造它們一定是五大金剛有缺陷,先看看它們之間的功能總結。
請思考:
- 元組的可讀性太差但不可修改,字典的可讀性強但可修改,兩者一結合便是命名元組 (namedtuple)。
- 在讀寫字典時,如果鍵不存在會報錯,那麼就有了預設字典 (defaultdict)。
- 計數器 (Counter) 是個字典的子類,能快速計量元素出現的個數。
- 清單隻能從尾部添加元素,不能從頭部,那麼就有了雙端隊列 (deque)。
- 在多個字典上操作效率不高,那麼就有了鍊式映射 (ChainMap)。
- 原來普通字典在讀取中不能記錄放入元素的順序(現在可以了),那麼就有了有序字典 (OrderedDict)。是以我覺得有序字典現在用處不大,但還是講講吧。
你看帶着這種思路學習新知識,是不是目的性更強一些,也更容易記住知識點。
1
namedtuple
命名元組 (namedtuple) 是元組和字典的混合體,它賦予每個元素含義 (字典的特性),而且元素不可修改 (元組的特性)。
建立命名元組需要設定兩個必需參數:元組名稱,字段名稱。
FP = namedtuple('FP', ['asset', 'instrument'])
FP
複制
__main__.FP
複制
其中 FP 是 Financial Product 的縮寫,我們發現 namedtuple 建立的和類 (class) 很像。
有了“類”,我們可以建立“對象”了,來試試建立一個外彙歐式期權(FX European Option),文法如下:
product = FP('FX', 'European Option')
product
複制
FP(asset='FX', instrument='European Option')
複制
像字典一樣檢視其鍵 (用 _fields),像元組一樣檢視其值的索引 (用 index) 和計數 (用 count)。
product._fields
複制
('asset', 'instrument')
複制
product.index('FX')
複制
複制
product.count('FX')
複制
1
複制
擷取元素有三種方法:
- 像元祖那樣用數值索引
- 像對象那樣用點 .
- 像對象那樣用函數 getattr
print( product[0] )
print( product.asset )
print( getattr(product, 'asset') )
複制
FX
FX
FX
複制
元組不可修改,命名元組也是,應該直接更新其元素會報錯。
product.asset = 'IR'
複制
但可以用 _replace 函數更新其值,并生成新的命名元組,比如把資産類别從外彙 FX 換到商品 CM。
product = product._replace(asset='CM')
product
複制
FP(asset='CM', instrument='European Option')
複制
用 _make 函數建立新的命名元組
product1 = FP._make(['IR', 'Basis Swap'])
product1
複制
FP(asset='IR', instrument='Basis Swap')
複制
還可以用字典打散的形式來建立命名元組
product2 = FP(**{'asset':'EQ', 'instrument':'Accumulator'})
product2
複制
FP(asset='EQ', instrument='Accumulator')
複制
2
defaultdict
預設字典 (defaultdict) 和字典不同,我們不需要檢查鍵是否存在,對于不存在的鍵,會賦一個預設值。
issubclass(defaultdict, dict)
複制
True
複制
建立預設字典時候記住要先設定好其值類型,用 int 舉例先。
nums = defaultdict(int)
nums['one'] = 1
nums['two'] = 2
nums['three'] = 3
print( nums['four'] )
print( nums )
複制
0
defaultdict(<class 'int'>, {'one': 1, 'two': 2, 'three': 3, 'four': 0})
複制
你看,字典 nums 沒有 'four' 這個鍵,是以給它賦予整型變量的預設值 0,如果是浮點型變量,那麼預設值是 0.0,如果是字元串,那麼預設值是 ''。
再把類型設為 list 來舉例。
def_dict = defaultdict(list)
def_dict['one'] = 1
def_dict['missing']
def_dict['another_missing'].append(4)
def_dict
複制
defaultdict(list, {'one': 1, 'missing': [], 'another_missing': [4]})
複制
當鍵為 'missing' 和 'another_missing' 時,空清單 [] 作為預設值賦給其鍵對應的值。
3
Counter
計數器 (Counter) 是字典的子類,提供了可哈希(hashable)對象的計數功能。可哈希就是可修改(mutable),比如清單就是可哈希或可修改。
issubclass(Counter, dict)
複制
True
複制
一個簡單例子來看 Counter 怎麼使用。
l = [1,2,3,4,1,2,6,7,3,8,1,2,2]
answer = Counter(l)
print(answer)
print(answer[2])
複制
Counter({2: 4, 1: 3, 3: 2, 4: 1, 6: 1, 7: 1, 8: 1})
4
複制
結果很直覺,不用解釋了。
4
deque
雙端隊列 (deque) 可讓我們從頭/尾兩端添加或删除元素。
首先建立雙端隊列。
deq = deque([1, 10.31, 'Python'])
print(deq)
複制
deque([1, 10.31, 'Python'])
複制
我們知道清單裡用 append,extend 和 pop 方法,它們隻能從尾部添加或删除元素,那麼在雙端隊列裡有 appendleft, extendleft 和 popleft 方法,從左邊,即尾部,添加或删除元素。看例子。
使用 append 和 appendleft,把參數來整塊添加。
deq.append('OK')
deq.appendleft(True)
print(deq)
複制
deque([True, 1, 10.31, 'Python', 'OK'])
複制
使用 extend 和 extendleft ,把參數來打散添加,清單 ['Go', 0] 打散成兩個元素添加上去,如果是 append 那麼就把這個清單當成整體添加上去。
deq.extend(['Go',0])
deq.extendleft([None,'I'])
print(deq)
複制
deque(['I', None, True, 1, 10.31, 'Python', 'OK', 'Go', 0])
複制
使用 pop 和 popleft 來删除元素。
p1 = deq.pop()
p2 = deq.popleft()
print(p1, p2)
print(deq)
複制
0 I
deque([None, True, 1, 10.31, 'Python', 'OK', 'Go'])
複制
5
ChainMap
鍊式映射 (ChainMap) 可看成字典的容器,将多個映射串聯起來,這樣它們就可以作為一個單元處理。通常比建立一個新字典和多次調用 update 函數要快很多。
首先建立鍊式映射,先建立三個字典,再把它們打包成 ChainMap。
toys = {'San Guo Sha': 30, 'Monopoly': 20}
computers = {'Mac': 1000, 'Lenovo': 800, 'Acer': 400}
clothing = {'Jeans': 40, 'Tees': 10}
複制
inventory = ChainMap( toys, computers, clothing )
inventory
複制
ChainMap({'San Guo Sha': 30, 'Monopoly': 20},
{'Mac': 1000, 'Lenovo': 800, 'Acer': 400},
{'Jeans': 40, 'Tees': 10})
複制
檢視鍵大富豪, 'Monopoly' 對應的值。
inventory['Monopoly']
複制
20
複制
如果多個字典都有某個鍵,那麼傳回第一個含該鍵的字典的值。
用 get() 函數檢視超級瑪麗,'Super Mario',如果每個字典都沒此鍵,不傳回值也不報錯。
inventory.get('Super Mario')
複制
删除三國殺, 'San Guo Sha'。
p = inventory.pop('San Guo Sha')
print(p)
inventory
複制
30
ChainMap({'Monopoly': 20},
{'Mac': 1000, 'Lenovo': 800, 'Acer': 400},
{'Jeans': 40, 'Tees': 10})
複制
在 Toy 字典中添加任天堂,'Nintendo'。
toys['Nintendo'] = 200
inventory
複制
ChainMap({'Monopoly': 20, 'Nintendo': 200},
{'Mac': 1000, 'Lenovo': 800, 'Acer': 400},
{'Jeans': 40, 'Tees': 10})
複制
6
OrderedDict
有序字典 (OrderedDict) 是字典的子類,就像正常字典一樣,它會記錄放入元素的順序,但現在正常字典也有這種功能了,是以有序字典的存在意義也不大了。
issubclass(OrderedDict, dict)
複制
True
複制
首先建立有序詞典和正常字典,發現兩者都是按着元素放入的順序來記錄的。
order = OrderedDict()
order['b'] = 1
order['a'] = 2
order['c'] = 3
print(order)
複制
OrderedDict([('b', 1), ('a', 2), ('c', 3)])
複制
unordered = dict()
unordered['b'] = 1
unordered['a'] = 2
unordered['c'] = 3
print(unordered)
複制
{'b': 1, 'a': 2, 'c': 3}
複制
在有序詞典中,有一個 reversed() 函數,可以逆序傳回字典的鍵。對比下面兩個例子。
for key in order:
print( key, ':', order[key] )
複制
b : 1
a : 2
c : 3
複制
for key in reversed(order):
print( key, ':', order[key] )
複制
c : 3
a : 2
b : 1
複制
結果很直覺,不解釋了。
7
總結
就一張圖,看那些連點的細節,不解釋。
Stay Tuned!