本文整理幾種基本容器:列表、元組、字典和集合的用法和collections中幾種已經預先實現的容器數據結構:namedtuple(),雙向鏈表deque,ChainMap,Counter,OrderedDict,defaultdict。其中,deque是一個雙向鏈表的結構,namedtuple是tuple的拓展,ChainMap,Counter,OrderedDict,defaultdict都可以視作是字典的拓展。
基本順序存儲結構——列表與元組
Python中的基本順序存儲結構是列表與元組,在操作的復雜度上和數組完全相同,其中列表是可變數據類型,元組是不可變數據類型。這里先介紹一下兩種數據結構共有的方法:
x in s |
返回布爾值,x在s中 |
x not in s |
返回布爾值,x不在s中為True |
s + t |
s和t的拼接,可以+= |
s * n or n * s |
n為int類型,返回s重復n次的結果。可以*=,同上 |
s[i] |
返回索引為i的元素 |
s[i:j(:k)] |
切片,返回子數組,從s[i]~s[i-1],k為步長,默認為1. |
len(s) |
現有元素個數 |
min(s) |
最小元素 |
max(s) |
最大元素 |
s.index(x[,i[,j]]) |
在s[i] ~ s[j-1]中,第一個大小為x的元素的索引,i,j默認0,-1 |
s.count(x) |
統計x的數目 |
這些都不涉及對數組內元素的更改,因此是列表和元組都可以使用的。注意元組只能使用以上的方法。
接下來介紹可變有序容器的方法,注意各種方法都有利用切片賦值的實現!
s[i] = x |
賦值操作 |
s[i:j(:k)] = t |
按照切片規則選擇元素進行賦值,注意如果t為[]可以用作刪除,如果i==j可以用作插入 |
del s[i:j(:k)] |
按照切片規則進行刪除 |
s.append(x) |
在尾部插入,等價於s.insert(I,len(s)),復雜度O(1). |
s.clear() |
清空所有元素 (等價於del s[:]) |
s.copy() |
創建一份s的淺拷貝 (等價於 s[:]) |
s.extend(t) or s += t |
在后面插入一組,相當於+=; |
s.insert(i, x) |
在i處插入x (等價於s[i:i] = [x]) |
s.pop([i]) |
默認刪除尾部元素,否則刪除i處的元素 |
s.remove(x) |
刪除第一個等於x的元素 |
s.reverse() |
列表反向 |
此外,列表還獨有s.sort(key=None, reverse=None),其中key是一個函數名,也就是比較函數,reverse為布爾值,True的話則會列表反向。
Sort單獨列出來是因為如果自己從可變有序容器里面派生的話並沒有這個方法。
基本哈希存儲結構——字典
字典是基於哈希表的一種映射結構,注意本文中介紹的Python的數據結構並沒有任何一種使用了紅黑樹的搜索結構。
len(d) |
返回字典元素個數 |
d[key] |
返回key對應的value |
d[key] = value |
為字典元素賦值,如果沒有則增加元素 |
del d[key] |
刪除字典元素 |
key in d/ key not in d |
查看key是否在d中 |
iter(d) |
返回一個迭代器,具有__next__()方法 |
clear() |
清空 |
copy() |
淺復制 |
fromkeys(seq[, value]) |
以seq作為鍵,value作為值建立字典,默認value為None |
get(key[, default]) |
安全的get方法,如果不存在返回default,如果不指定default則報錯 |
items() |
列出一個鍵值對的view |
keys() |
列出key的view,通常用於遍歷 |
values() |
Return a new view of the dictionary's values. |
pop(key[, default]) |
Key在d中則刪除,和del[key]一樣,default如果被指定,那么當不存在key的時候會返回default而不會報錯 |
popitem() |
彈出一個鍵值對,為key的哈希序列中的第一個 |
setdefault(key[, default]) |
安全的添加操作,如果存在就返回value不更改值,如果不存在添加一個key:default的表項,default默認為0 |
update([other]) |
更改操作,other可以是鍵值對的列表或元組(二級的),也可以是字典,用other中的鍵值對添加到或替換原有鍵值對 |
鍵值相等的字典——集合set與frozenset
Python中由set和frozenset兩種集合結構。由於set中的元素既是鍵又是值,所以他的內容必須是不可修改的,frozenset就是被聲明為常量的set,,用於建立集合的集合
首先介紹兩種集合共有的方法:
注意用的都是邏輯運算符進行衍生而不是+
len(s) |
集合元素個數 |
x in/not in s |
是否在集合中 |
isdisjoint(other) |
和other的交集為空 |
set <= other |
包含於 |
set < other |
真包含於 |
set >= other |
包含 |
set > other |
真包含 |
set | other | ... |
並 |
set & other & ... |
交 |
set - other - ... |
差 |
set ^ other |
對稱差(並-交) |
copy() |
淺拷貝 |
而set獨有的方法是,一個是諸如&=之類的運算符,還有:
add(elem) |
增加元素 |
remove(elem) |
刪除元素,不存在則報錯 |
discard(elem) |
刪除元素,不存在不報錯 |
pop() |
刪除哈希表第一個元素,空集報錯 |
clear() |
清空 |
Tuple的拓展的生成器collections.namedtuple(typename, field_names, verbose=False, rename=False)
這是一個生成器,本身並不是類。它生成的就是有名字的元組類型,很適合於存儲表條目,例如下面的代碼:
Point = namedtuple('Point', ['x', 'y'])
這樣做,Point會定義一個具有x和y成員的類,想要調用相應對象的x屬性,既可以使用p[0],也可以使用p.x。注意雖然叫namedtuple,但是其本身是可以更改的,可以視作是一種簡單的結構體的定義方式。可以使用p=Point(1,2)進行定義
如果verbose為真,那么會將定義過程顯示的輸出到控制台;如果rename為真,屬性中不符合命名規則的屬性會被自動更名為'_index',如一些保留字或者數字開頭。
利用這種有名字的生成器,可以使程序更加簡潔,並且更加易讀,用它作為value再來建字典就非常的方便了。
有幾個內建方法,比如:
_make(iterable):使用一個可迭代的對象建立namedtuple,例如列表。
_asdict():轉化為一個字典(Py3.1后為OrderedDict)
利用字符串格式的屬性名找出值getattr(p,'x');
dict轉化為Tuple要利用Unpacking語法 **args,即Point(**somedict),**語法會把dict切分開,注意此語法只發生在參數傳遞過程中才是有效的。
一個*是把容器中的元素一個一個傳遞,兩個*是專門用於解包字典,鍵值對應使用。
還有內置屬性可以以字符串的形式輸出參數的名字 ._fields
字典組ChainMap
定義時候,后邊按元組的形式接受參數ChainMap(*map),每一個參數都應該是一個字典名,最后返回一個字典組。
建立之后,其中有maps屬性,是所有的字典組成的列表;parents屬性,是除了第一個字典之外的所有字典的列表。
內建的new_child(m=None)方法,是添加一個新的字典到列表中作為第一個字典,等價於ChainMap(m,*d.maps).
字典組擁有字典的各種操作,它的特點是所有涉及到更新的操作如插入刪除等都是只針對第一個字典,而查找的操作則是按照順序對全部字典進行查找。如果希望更新操作也是對全部字典,則可以使用它進行派生,並且對maps使用for循環。
計數器Counter(本質是C++multiset、sql bag)
可以認為他是一個key-int字典,不過他實際上是一個multiset!,int默認為0,有幾個創建方法:
從一般可迭代對象c = Counter('gallahad'),從字典c = Counter({'red': 4, 'blue': 2}),直接指明(字典也可以這樣初始化,是底層方法)c = Counter(cats=4, dogs=8) 。
它具有字典的全部操作,同時又添加了一下計數器的很自然的操作。
.elements():列表形式一個一個列出元素。
.most_common([n]):列出出現次數最多的前n個元素的列表,如果n沒有,則按順序列出所有的。
.subtrackc(other):兩個計數器相減,可以出現0或負數。可以用+c 這一語句清除0和負數
四種運算:+,-,&,|,按key的加減,按multiset取交和並,注意出現的負數和0會被清楚
特殊字典OrderedDict和defaultdict
這兩個都具有普通字典的全部功能,不過有一些不同:
defaultdict(class)
指定了值的類型,會用這種類型自動構造,相當於可以提前使用這種類的方法,一個有趣的例子是:
>>> s = [('red', 1), ('blue', 2), ('red', 3), ('blue', 4), ('red', 1), ('blue', 4)]
>>> d = defaultdict(set)
>>> for k, v in s:
... d[k].add(v)
...
>>> sorted(d.items())
[('blue', {2, 4}), ('red', {1, 3})]
class collections.OrderedDict([items])
這種容器不光存儲了鍵值對,還維護了被加入字典的順序,使它能夠像棧一樣彈出,增加了兩個方法:
.popitem(last = True): 移除最后一個加入的鍵值對,如果last=false則移除第一個加入的鍵值對。
.move_to_end(key,last = True): 將某一個鍵的條目作為最后一個加入的(false->作為最先加入的)
這個數據類型可以簡單地實現LRU的算法,可以通過一個key:(value,forekey,nextkey)的字典結構實現此類。
雙向鏈表deque
class collections.deque([iterable[, maxlen]])
就是簡單意義上的雙向鏈表(雙向隊列),注意這里不再支持切片操作了,隨機訪問的操作保留,可以通過隨機訪問通過del實現刪除,但是沒有切片無法用於替換,截取等操作,無法sort:
append(x)¶ |
右側插入 |
appendleft(x) |
左側插入 |
clear() |
清空 |
copy() |
淺復制 |
count(x) |
計數 |
extend(iterable) |
按組右側連接 |
extendleft(iterable) |
按組左側連接 |
index(x[, start[, stop]]) |
搜索等於x的第一個位置 |
insert(i, x) |
在i處插入x |
pop() |
右側刪除 |
popleft() |
左側刪除 |
remove(value) |
搜索第一個元素並刪除 |
reverse() |
隊列反向 |
rotate(n) |
前n個隊列的尾部元素移動到首部 |
maxlen |
有界情況下返回最大長度 |
注意其沒有截取,切片等操作,只能通過copy,pop的方式實現截取操作。