1. 程式人生 > >python資料結構進階

python資料結構進階

文章目錄


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