python資料結構進階
文章目錄
- namedtuple
- deque雙端佇列
- 建立雙向佇列
- append(往右邊新增一個元素)
- appendleft(往左邊新增一個元素)
- pickle 儲存佇列
- clear(清空佇列)
- copy(淺拷貝)
- count(返回指定元素的出現次數)
- extend(從佇列右邊擴充套件一個列表的元素)
- extendleft(從佇列左邊擴充套件一個列表的元素)
- index(查詢某個元素的索引位置)
- insert(在指定位置插入元素)
- pop(獲取最右邊一個元素,並在佇列中刪除)
- popleft(獲取最左邊一個元素,並在佇列中刪除)
- remove(刪除指定元素)
- reverse(佇列反轉)
- rotate(把右邊元素放到左邊)
- 練習:猜數字大小
- defaultdict
- OrderedDict 有序字典
- Counter
- Queue佇列
collections是Python內建的一個集合模組,提供了許多有用的集合類。
namedtuple
我們知道tuple可以表示不變集合,例如,一個點的二維座標就可以表示成
p = (1, 2)
p
(1, 2)
看到(1, 2),很難看出這個tuple是用來表示一個座標的
義一個class又小題大做了,這時,namedtuple就派上了用場:
from collections import namedtuple
Point = namedtuple('Point' , ['x', 'y'])
p = Point(1, 2)
print(p.x)
print(p.y)
print(p[0])
print(p[1])
1
2
1
2
namedtuple是一個函式,它用來建立一個自定義的tuple物件,並且規定了tuple元素的個數,並可以用屬性而不是索引來引用tuple的某個元素。
這樣一來,我們用namedtuple可以很方便地定義一種資料型別,它具備tuple的不變性,又可以根據屬性來引用,使用十分方便。
可以驗證建立的Point物件是tuple的一種子類
print(isinstance(p, Point))
print(isinstance(p, tuple))
print(type(p))
True
True
<class '__main__.Point'>
類似的,如果要用座標和半徑表示一個圓,也可以用namedtuple定義:
# namedtuple('名稱', [屬性list]):
Circle = namedtuple('Circle', ['x', 'y', 'r'])
c = Circle(1,2,3)
print(type(c))
print(c)
<class '__main__.Circle'>
Circle(x=1, y=2, r=3)
deque雙端佇列
使用list儲存資料時,按索引訪問元素很快,但是插入和刪除元素就很慢了,因為list是線性儲存,資料量大的時候,插入和刪除效率很低。
deque是為了高效實現插入和刪除操作的雙向列表,適合用於佇列和棧:
建立雙向佇列
from collections import deque
d = deque(maxlen=2)
print(d)
d = deque([],3)
print(d)
deque([], maxlen=2)
deque([], maxlen=3)
append(往右邊新增一個元素)
d.append('x')
d.append('y')
d
deque(['x', 'y'])
appendleft(往左邊新增一個元素)
d.appendleft('z')
d
deque(['z', 'x', 'y'])
d.append('s')
print(d)
deque(['x', 'y', 's'], maxlen=3)
pickle 儲存佇列
dump(object, file)
import pickle
#將對d象存在了temp.pkl這個檔案中
d1 = {1: 'One', 2: 'Two', 3: 'Three'}
f1 = open('temp.pkl', 'wb')
pickle.dump(d, f1,True)
pickle.dump(d1, f1,True)
f1.close()
load(file) -> object
f2 = open('temp.pkl', 'rb')
d2 = pickle.load(f2)
print(d2)
d2 = pickle.load(f2)
print(d2)
f2.close()
deque(['x', 'y', 's'], maxlen=3)
{1: 'One', 2: 'Two', 3: 'Three'}
dumps(object) -> string
#True ,則該引數指定用更快以及更小的二進位制表示來建立 pickle
p1= pickle.dumps(d,True)
p1
b'}q\x00(K\x01X\x03\x00\x00\x00Oneq\x01K\x02X\x03\x00\x00\x00Twoq\x02K\x03X\x05\x00\x00\x00Threeq\x03u.'
loads(string) -> object
t2 = pickle.loads(p1)
t2
{1: 'One', 2: 'Two', 3: 'Three'}
clear(清空佇列)
import collections
d = collections.deque()
d.append(1)
d.clear()
print(d)
deque([])
copy(淺拷貝)
import collections
d = collections.deque()
d.append(1)
new_d = d.copy()
d.clear()
print(new_d)
deque([1])
import collections
d = collections.deque()
d.append(1)
new_d = d
d.clear()
print(new_d)
deque([])
count(返回指定元素的出現次數)
import collections
d = collections.deque()
d.append(1)
d.append(1)
print(d.count(1))
2
extend(從佇列右邊擴充套件一個列表的元素)
import collections
d = collections.deque()
d.append(1)
d.extend([3,4,5])
print(d)
deque([1, 3, 4, 5])
extendleft(從佇列左邊擴充套件一個列表的元素)
import collections
d = collections.deque()
d.append(1)
d.extendleft([3,4,5])
print(d)
deque([5, 4, 3, 1])
index(查詢某個元素的索引位置)
import collections
d = collections.deque()
d.extend(['a','b','c','d','e'])
print(d)
print(d.index('e'))
print(d.index('c',0,4)) #指定查詢區間
deque(['a', 'b', 'c', 'd', 'e'])
4
2
insert(在指定位置插入元素)
import collections
d = collections.deque()
d.extend(['a','b','c','d','e'])
d.insert(2,'z')
print(d)
deque(['a', 'b', 'z', 'c', 'd', 'e'])
pop(獲取最右邊一個元素,並在佇列中刪除)
import collections
d = collections.deque()
d.extend(['a','b','c','d','e'])
x = d.pop()
print(x,d)
x = d.pop()
print(x,d)
e deque(['a', 'b', 'c', 'd'])
d deque(['a', 'b', 'c'])
popleft(獲取最左邊一個元素,並在佇列中刪除)
import collections
d = collections.deque()
d.extend(['a','b','c','d','e'])
x = d.popleft()
print(x,d)
x = d.popleft()
print(x,d)
a deque(['b', 'c', 'd', 'e'])
b deque(['c', 'd', 'e'])
remove(刪除指定元素)
import collections
d = collections.deque()
d.extend(['a','b','c','d','e'])
d.remove('c')
print(d)
deque(['a', 'b', 'd', 'e'])
reverse(佇列反轉)
import collections
d = collections.deque()
d.extend(['a','b','c','d','e'])
d.reverse()
print(d)
deque(['e', 'd', 'c', 'b', 'a'])
rotate(把右邊元素放到左邊)
import collections
d = collections.deque()
d.extend(['a','b','c','d','e'])
d.rotate(2) #指定次數,預設1次
print(d)
deque(['d', 'e', 'a', 'b', 'c'])
import collections
d = collections.deque()
d.extend(['a','b','c','d','e'])
d.rotate(3) #指定次數,預設1次
print(d)
deque(['c', 'd', 'e', 'a', 'b'])
練習:猜數字大小
import random
import collections
number = random.randint(0,100)
history = collections.deque(maxlen = 5)
def guess(n):
if n == number:
print("right")
return True
elif n < number:
print("%s is less than the number " % n)
elif n > number:
print("%s is greater than the number " % n)
return False
count = 0
while count < 6:
line = input("please input a number:")
if line.isdigit():
n = int(line)
history.append(n)
if n > 100:
print("attention the range between [0 ,100]")
else:
if guess(n):
break
elif line == "history" or line == "h?" or line == "h":
print(list(history))
else:
print("please input a integer")
count += 1
else:
print("you have tried too many times..fuck off")
please input a number:1
1 is less than the number
please input a number:1
1 is less than the number
please input a number:1
1 is less than the number
please input a number:1
1 is less than the number
please input a number:1
1 is less than the number
please input a number:1
1 is less than the number
you have tried too many times..fuck off
defaultdict
使用dict時,如果引用的Key不存在,就會丟擲KeyError。如果希望key不存在時,返回一個預設值,就可以用defaultdict:
from collections import defaultdict
dd = defaultdict(lambda: 'N/A')
dd['key1'] = 'abc'
print(dd['key1']) # key1存在
print(dd['key2']) # key2不存在,返回預設值
abc
N/A
adict = {'a':'sun','b':'cheng','c':'quan'}
print(adict.get('a')) #獲取鍵‘a’對應的值
print(adict.get('d'))
sun
None
adict = {'a':'sun','b':'cheng','c':'quan'}
adict['d']
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-27-9173c523be71> in <module>
1 adict = {'a':'sun','b':'cheng','c':'quan'}
----> 2 adict['d']
KeyError: 'd'
注意預設值是呼叫函式返回的,而函式在建立defaultdict物件時傳入。
除了在Key不存在時返回預設值,defaultdict的其他行為跟dict是完全一樣的。
OrderedDict 有序字典
使用dict時,Key是無序的。在對dict做迭代時,我們無法確定Key的順序。
如果要保持Key的順序,可以用OrderedDict:
from collections import OrderedDict
d = dict([('a', 1), ('c', 2), ('b', 3),('d', 2),('e', 2)])
print(list(d.keys())) # dict的Key是無序的
od = OrderedDict([('a', 1), ('c', 2), ('b', 3),('d', 2),('e', 2)])
print(od)# OrderedDict的Key是有序的
od = OrderedDict()
od['z'] = 1
od['y'] = 2
od['x'] = 3
list(od.keys()) # 按照插入的Key的順序返回
['a', 'c', 'b', 'd', 'e']
OrderedDict([('a', 1), ('c', 2), ('b', 3), ('d', 2), ('e', 2)])
['z', 'y', 'x']
%%python2
a={'key1':'a','key2':'b','key3':'c','key4':'d','key5':'e'}
print(a)
{'key3': 'c', 'key2': 'b', 'key1': 'a', 'key5': 'e', 'key4': 'd'}
!python -V
Python 3.7.0
Counter
Counter是一個簡單的計數器,例如,統計字元出現的個數:
from collections import Counter
c = Counter()
for ch in 'programming':
c[ch] = c[ch] + 1
print(c)
Counter({'r': 2, 'g': 2, 'm': 2, 'p': 1, 'o': 1, 'a': 1, 'i': 1, 'n': 1})
Counter實際上也是dict的一個子類,上面的結果可以看出,字元’g’、‘m’、'r’各出現了兩次,其他字元各出現了一次。
Queue佇列
Python的Queue模組提供一種適用於多執行緒程式設計的FIFO實現。它可用於在生產者(producer)和消費者(consumer)之間執行緒安全(thread-safe)地傳遞訊息或其它資料,因此多個執行緒可以共用同一個Queue例項。Queue的大小(元素的個數)可用來限制記憶體的使用。
Basic FIFO Queue
Queue類實現了一個基本的先進先出(FIFO)容器,使用put()將元素新增到序列尾端,get()從佇列尾部移除元素。
from queue import Queue
q = Queue(maxsize=10)
for i in range(3):
q.put(i)
while not q.empty():
print(q.get())
0
1
2
LIFO Queue
與標準FIFO實現Queue不同的是,LifoQueue使用後進先出序(會關聯一個棧資料結構)
from queue import LifoQueue
q = LifoQueue(maxsize=10)
for i in range(3):
q.put(i)
while not q.empty():
print(q.get())
2
1
0
Priority Queue(優先佇列)
除了按元素入列順序外,有時需要根據佇列中元素的特性來決定元素的處理順序。例如,財務部門的列印任務可能比碼農的程式碼列印任務優先順序更高。PriorityQueue依據佇列中內容的排序順序(sort order)來決定那個元素將被檢索
from queue import PriorityQueue
class Job(object):
def __init__(self, priority, description):
self.priority = priority
self.description = description
print('New job:', description)
def __lt__(self, other):
return self.priority < other.priority
q = PriorityQueue(maxsize=10)
q.put(Job(5, 'Mid-level job'))
q.put(Job(10, 'Low-level job'))
q.put(Job(1, 'Important job'))
while not q.empty():
next_job = q.get()
print('Processing job', next_job.description)
New job: Mid-level job
New job: Low-level job
New job: Important job
Processing job Important job
Processing job Mid-level job
Processing job Low-level job