-
Queue 簡介
- Queue 叫隊列,是數據結構中的一種,基本上所有成熟的編程語言都內置了對 Queue 的支持。
- Python 中的 Queue 模塊實現了多生產者和多消費者模型,當需要在多線程編程中非常實用。而且該模塊中的 Queue 類實現了鎖原語,不需要再考慮多線程安全問題
- 該模塊內置了三種類型的 Queue,分別是
class queue.Queue(maxsize=0),class queue.LifoQueue(maxsize=0)和class queue.PriorityQueue(maxsize=0)。它們三個的區別僅僅是取出時的順序不一致而已。 -
Queue 是一個 FIFO 隊列,任務按照添加的順序被取出。
LifoQueue 是一個 LIFO 隊列,類似堆棧,后添加的任務先被取出。
PriorityQueue 是一個優先級隊列,隊列里面的任務按照優先級排序,優先級高的先被取出。
-
Queue 常用操作
-
類和異常
-
1 class queue.Queue(maxsize=0) 2 class queue.LifoQueue(maxsize=0) 3 class queue.PriorityQueue(maxsize=0)
- 如你所見,就是上面所說的三種不同類型的內置隊列,其中 maxsize 是個整數,用於設置可以放入隊列中的任務數的上限。當達到這個大小的時候,插入操作將阻塞至隊列中的任務被消費掉。如果 maxsize 小於等於零,則隊列尺寸為無限大。
-
1 exception queue.Empty 2 # 對空的 Queue 對象調用非阻塞的 get() (or get_nowait()) 時,會引發該異常。 3 4 exception queue.Full 5 # 對滿的 Queue 對象調用非阻塞的 put() (or put_nowait()) 時,會引發該異常。
-
常用操作
-
添加任務
- 向隊列中添加任務,直接調用
put()函數即可 -
1 import queue 2 >>> q = queue.Queue(maxsize=1) 3 >>> q.put(100)
put()函數完整的函數簽名如下Queue.put(item, block=True, timeout=None),如你所見,該函數有兩個可選參數。- 默認情況下,在隊列滿時,該函數會一直阻塞,直到隊列中有空余的位置可以添加任務為止。如果 timeout 是正數,則最多阻塞 timeout 秒,如果這段時間內還沒有空余的位置出來,則會引發
Full異常。 -
1 >>> import queue 2 >>> q = queue.Queue(maxsize=1) 3 >>> q.put(100) 4 >>> q.put(100,True,2) 5 Traceback (most recent call last): 6 File "<stdin>", line 1, in <module> 7 File "E:\Python37-32\lib\queue.py", line 147, in put 8 raise Full 9 queue.Full # 創建一個容量為 1 的隊列,2 秒內沒有位置添加任務則引發 Full 異常 10 >>> q.put(100) # 該方法會一直阻塞
- 當 block 為 false 時,timeout 參數將失效。同時如果隊列中沒有空余的位置可添加任務則會引發
Full異常,否則會直接把任務放入隊列並返回,不會阻塞。 -
1 >>> import queue 2 >>> q = queue.Queue(maxsize=1) 3 >>> q.put(100) 4 >>> q.put(100,False,2) 5 Traceback (most recent call last): 6 File "<stdin>", line 1, in <module> 7 File "E:\Python37-32\lib\queue.py", line 136, in put 8 raise Full 9 queue.Full 10 # 創建一個容量為 1 的隊列,在第二次放入任務時指定為非阻塞模式,則會立刻引發 Full 異常
- 另外,還可以通過
Queue.put_nowait(item)來添加任務,相當於Queue.put(item, False),不再贅述。同樣,在隊列滿時,該操作會引發Full異常。 -
獲取任務
- 從隊列中獲取任務,直接調用
get()函數即可 -
1 >>> import queue 2 >>> q = queue.Queue() 3 >>> q.put(100) 4 >>> q.get() 5 100
- 與
put()函數一樣,get()函數也有兩個可選參數,完整簽名如下Queue.get(block=True, timeout=None)。 - 默認情況下,當隊列空時調用該函數會一直阻塞,直到隊列中有任務可獲取為止。如果 timeout 是正數,則最多阻塞 timeout 秒,如果這段時間內還沒有任務可獲取,則會引發
Empty異常。 -
1 >>> import queue 2 >>> q = queue.Queue() 3 >>> q.put(100) 4 >>> q.get() 5 100 6 >>> q.get(True,2) 7 Traceback (most recent call last): 8 File "<stdin>", line 1, in <module> 9 File "E:\Python37-32\lib\queue.py", line 178, in get 10 raise Empty 11 _queue.Empty # 2 秒鍾內沒有任務可獲取則引發 Empty 異常 12 >>> q.get() # 該方法會一直阻塞
- 當 block 為 false 時,timeout 參數將失效。同時如果隊列中沒有任務可獲取則會立刻引發
Empty異常,否則會直接獲取一個任務並返回,不會阻塞。 -
1 >>> import queue 2 >>> q = queue.Queue() 3 >>> q.put(100) 4 >>> q.get() 5 100 6 >>> q.get(False,2) 7 Traceback (most recent call last): 8 File "<stdin>", line 1, in <module> 9 File "E:\Python37-32\lib\queue.py", line 167, in get 10 raise Empty 11 _queue.Empty 12 # 指定為非阻塞模式,隊列為空則立即引發 Empty 異常
- 另外,還可以通過
Queue.get_nowait()來獲取任務,相當於Queue.get(False),不再贅述。同樣,在隊列為空時,該操作會引發Empty異常。 -
其他常用方法
-
獲取隊列大小
-
1 >>> import queue 2 >>> q = queue.Queue() 3 >>> q.put(100) 4 >>> q.put(200) 5 >>> q.qsize() 6 2
-
判斷隊列是否空
- 如果隊列為空,返回
True,否則返回False。如果 empty() 返回True,不保證后續調用的 put() 不被阻塞。類似的,如果 empty() 返回False,也不保證后續調用的 get() 不被阻塞。 -
判斷隊列是否滿
- 如果隊列是滿的返回
True,否則返回False。如果 full() 返回True不保證后續調用的 get() 不被阻塞。類似的,如果 full() 返回False也不保證后續調用的 put() 不被阻塞。 -
1 >>> import queue 2 >>> q = queue.Queue(maxsize=1) 3 >>> q.empty() 4 True 5 >>> q.full() 6 False 7 >>> q.put(100) 8 >>> q.empty() 9 False 10 >>> q.full() 11 True
-
隊列對比
-
FIFO 隊列
queue.Queue()是 FIFO 隊列,出隊順序跟入隊順序是一致的。-
1 import queue 2 q = queue.Queue() 3 for index in range(10): 4 q.put(index) 5 while not q.empty(): 6 print(q.get(), end=", ") 7 ## 輸出結果如下 8 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
-
LIFO 隊列
queue.LifoQueue()是 LIFO 隊列,出隊順序跟入隊順序是完全相反的,類似於棧。-
import queue q = queue.LifoQueue() # 創建一個 LIFO 隊列 for index in range(10): q.put(index) while not q.empty(): print(q.get(), end=", ") ## 輸出結果如下 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
-
優先級隊列
優先級隊列中的任務順序跟放入時的順序是無關的,而是按照任務的大小來排序,最小值先被取出。那任務比較大小的規則是怎么樣的呢。
-
如果是內置類型,比如數值或者字符串,則按照自然順序來比較排序。
-
import queue q = queue.PriorityQueue() for index in range(10,0,-1): q.put(index) while not q.empty(): print(q.get(), end=", ") ## 輸出結果如下 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
-
如果是列表或者元祖,則先比較第一個元素,然后比較第二個,以此類推,直到比較出結果
-
import queue q = queue.PriorityQueue() q.put(["d","b"]) q.put(["c","b"]) while not q.empty(): print(q.get(), end=", ") ## 輸出結果如下 ['c', 'b'], ['d', 'b'],
- 注意,因為列表的比較對規則是按照下標順序來比較的,所以在沒有比較出大小之前 ,隊列中所有列表對應下標位置的元素類型要一致。
- 好比
[2,1]和["1","b"]因為第一個位置的元素類型不一樣,所以是沒有辦法比較大小的,所以也就放入不了優先級隊列。 - 然而對於
[2,1]和[1,"b"]來說即使第二個元素的類型不一致也是可以放入優先級隊列的,因為只需要比較第一個位置元素的大小就可以比較出結果了,就不需要比較第二個位置元素的大小了。 - 但是對於
[2,1]和 1[2,"b"]來說,則同樣不可以放入優先級隊列,因為需要比較第二個位置的元素才可以比較出結果,然而第二個位置的元素類型是不一致的,無法比較大小。 - 綜上,也就是說,直到在比較出結果之前,對應下標位置的元素類型都是需要一致的。
-
如果是自定義類型,需要實現
__lt__比較函數 - 下面我們自定義一個動物類型,希望按照年齡大小來做優先級排序。年齡越小優先級越高。
-
import queue q = queue.PriorityQueue() class Animal: def __init__(self, age, name): self.age = age self.name = name def __lt__(self, other): # 實現 < 操作 return self.age < other.age # 如果將 < 變成 > 則相當於逆序 q.put(Animal(3,"cat")) q.put(Animal(2,"dog")) while not q.empty(): animal = q.get() print(animal.name, animal.age, end=", ") ## 輸出結果如下 dog 2, cat 3,
