集合相關的模塊都在這裏了……提起Python應該沒有不知道這個模塊的吧,它爲開發者提供了一系列敏捷、實用的類和方法——有的是爲了對集合進行相關操作,有的本身就是集合
概述:collections模塊有哪些東西
使用__file__
屬性用來看看source code的位置
>>> import collections
>>> collections.__file__
'/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/collections/__init__.py'
打開idle並導入collections模塊,看看都有哪些有趣的東西
一看還不少……其中collections.abc
(沒錯,是collections的一個子模塊)模塊是在py3中被新引進的(我們將在本文最後對其做簡單介紹),然後比較常用的有namedtuple
、deque
、defaultdict
、OrderedDict
、Counter
namedtuple(具名元組)
看名字就知道首先namedtuple是一個tuple,其次它……有名字 :-)
namedtuple
是一個函數,作用是創建命名元組(廢話!)——也就是創建一個只有類名和屬性卻不包括方法的簡單類(按照官方的說法,是一個tuple-like的對象
collections.namedtuple(typename, field_names, *, verbose=False, rename=False, module=None)
通常我們關注前兩個參數,第一個是具名元組的類名,第二個參數是它的屬性名;在實際書寫時既可以把多個屬性放在一個字符串中,屬性之間用空格或逗號分開,也可以把第二個參數寫成一個序列(如tuple、list)形式並把多個屬性分別包裹在單引號中它們之間用逗號分開;如圖
>>> from collections import namedtuple
>>> Point = namedtuple('Point', 'x y')
>>> x = Point(1,2)
>>> x
Point(x=1, y=2)
>>> Person = namedtuple('Person', 'name, age')
>>> p = Person('Xiaoming', 18)
>>> p
Person(name='Xiaoming', age=18)
>>> Item = namedtuple('Item', ['name', 'price'])
>>> iphone = Item('cellphone', '4999')
>>> iphone
Item(name='cellphone', price='4999')
另外,namedtuple對象是隻讀(read-only)的
deque(雙向隊列)
光看名字不知爲何物,其實deque全稱應該是double-ended queue,也就是雙向隊列的意思(學過數據結構基礎的朋友應該比較熟悉
deque
彌補了list
插入、刪除的效率問題(list類似數組結構,擅長查找
deque
是一個類(雖然類名小寫。。),以下是它的構造
class collections.deque([iterable[, maxlen]])
接受一個可迭代對象(內置的就是str、tuple、list咯),第二個參數指定最大隊列長度(maxlen)
讓我們看看deque
類都有那些方法
有append()
、appendleft()
、clear()
、copy()
……這些方法完全不需要死記,只需要明白一點:deque
是雙向隊列,是list
的完善,所以列表支持的操作它都支持而且還可以從兩端的任意端插入新元素(對xxxleft()
方法留個心眼即可)
defaultdict(默認字典)
又是一個小寫的類名(我猜也許是因爲考慮到init函數就把該類當函數用了
class collections.defaultdict([default_factory[, ...]])
collections.defaultdict
是__builins__.dict
的子類,在其父類的基礎上補充了魔法方法__missing__(key)
If the default_factory attribute is None, this raises a KeyError exception with the key as argument.
If default_factory is not None, it is called without arguments to provide a default value for the given key, this value is inserted in the dictionary for the key, and returned.
If calling default_factory raises an exception this exception is propagated unchanged.
This method is called by the __getitem__() method of the dict class when the requested key is not found; whatever it returns or raises is then returned or raised by __getitem__().
Note that __missing__() is not called for any operations besides __getitem__(). This means that get() will, like normal dictionaries, return None as a default rather than using default_factory.
也就是說,如果訪問的key不存在或訪問拋出異常時(比如下面的例子,是一個KeyError)會返回指定內容(__missing__函數的作用)——defaultdict改變的是value的狀態,觸發條件是KeyError
下面的例子來自Python官方例程(略有改動
上面的例子中,使用defaultdict打破了key-value一一對應的固有模式,雖然還是一一對應,但是後來對應的value變成了list,屬於容器類型,出發的原因是KeyError,具體事因爲dict不支持append操作——於是轉而調用list的append操作,效果比較明顯
該技巧類似於dict_obj.setdefault(k, []).append(v)
defaultdict
的另一個應用是讓字典具有計數的功能(揹包屬性),代碼見下圖
如果是使用普通版本的字典{}
,會在第一個item的方法就raise一個KeyError,因爲key是不可變的(總之,KeyError的導致是多方面的
上面兩個例子只是defaultdict的廣泛應用中的小小一瞥,可見defaultdict也是一個較爲強大的工具;使用的關鍵是第一個參數的傳遞,即所謂的『默認工廠函數』(default_factory)
OrderedDict(有序字典)
正如其名,有序字典是有序的;它也是內置dict
的子類(所以也支持一切dict擁有的操作集)
class collections.OrderedDict([items])
我們知道,普通的字典dict(..)
返回的對象是無序的(和插入順序無關,這一點很容易驗證
但是OrderedDict就沒有這個問題,它會按照用戶插入的順序排序而不是key的排序
鑑於這樣的特性,OrderedDict可以做成一個FIFO的隊列(當元素超過上限時刪除最先進入的元素
class LastUpdatedOrderedDict(OrderedDict):
def __init__(self, capacity):
super(LastUpdatedOrderedDict, self).__init__()
self._capacity = capacity
def __setitem__(self, key, value):
containsKey = 1 if key in self else 0
if len(self) - containsKey >= self._capacity:
last = self.popitem(last=False)
print('remove:', last)
if containsKey:
del self[key]
print('set:', (key, value))
else:
print('add:', (key, value))
OrderedDict.__setitem__(self, key, value)
Counter類
collections.Counter
類用來計數,類似於MultiSet或Bag
class collections.Counter([iterable-or-mapping])
和defaultdict
、OrderedDict
類一樣,都是dict
類的子類
源碼可以在本文的超鏈接處點開找到
下面是一個出自《改善Python代碼的91個建議》這本書的一個例子
>>> some_data = ['a', '2', 2, 4, 5, '2', 'b', 4, 7, 'a', 5, 'd', 'a', 'z']
>>> from collections import Counter
>>> print(Counter(some_data))
Counter({'a': 3, '2': 2, 4: 2, 5: 2, 2: 1, 'b': 1, 7: 1, 'd': 1, 'z': 1})
你可以像上面把一個序列作爲參數來構造一個Counter對象,默認下Counter對象(記住,是dict的子類)的key是待計數元素,value是對應的計數;也可以動態的使用循環對序列進行計數
下面是另一個用Counter計數的例子
>>> from collections import Counter
>>> c = Counter()
>>> for ch in 'hello world':
c[ch] += 1
>>> print(c)
Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1})
先是創建一個空的Counter對象,接着對其進行填充