-
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,