Python100天学习计划:第51天: Python Queue 入门

by 豆豆

Python Queue 入门

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 常用操作

类和异常

class queue.Queue(maxsize=)
class queue.LifoQueue(maxsize=)
class queue.PriorityQueue(maxsize=)

如你所见,就是上面所说的三种不同类型的内置队列,其中 maxsize 是个整数,用于设置可以放入队列中的任务数的上限。当达到这个大小的时候,插入操作将阻塞至队列中的任务被消费掉。如果 maxsize 小于等于零,则队列尺寸为无限大。

exception queue.Empty
# 对空的 Queue 对象调用非阻塞的 get() (or get_nowait()) 时,会引发该异常。

exception queue.Full
# 对满的 Queue 对象调用非阻塞的 put() (or put_nowait()) 时,会引发该异常。

常用操作

1、添加任务

向队列中添加任务,直接调用 put() 函数即可

>>> import queue
>>> q = queue.Queue(maxsize=1)
>>> q.put(100)

put() 函数完整的函数签名如下 Queue.put(item, block=True, timeout=None),如你所见,该函数有两个可选参数。

默认情况下,在队列满时,该函数会一直阻塞,直到队列中有空余的位置可以添加任务为止。如果 timeout 是正数,则最多阻塞 timeout 秒,如果这段时间内还没有空余的位置出来,则会引发 Full 异常。

>>> import queue
>>> q = queue.Queue(maxsize=1)
>>> q.put(100)
>>> q.put(100,True,2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "E:\Python37-32\lib\queue.py", line 147, in put
    raise Full
queue.Full # 创建一个容量为 1 的队列,2 秒内没有位置添加任务则引发 Full 异常
>>> q.put(100) # 该方法会一直阻塞

当 block 为 false 时,timeout 参数将失效。同时如果队列中没有空余的位置可添加任务则会引发 Full 异常,否则会直接把任务放入队列并返回,不会阻塞。

>>> import queue
>>> q = queue.Queue(maxsize=1)
>>> q.put(100)
>>> q.put(100,False,2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "E:\Python37-32\lib\queue.py", line 136, in put
    raise Full
queue.Full
# 创建一个容量为 1 的队列,在第二次放入任务时指定为非阻塞模式,则会立刻引发 Full 异常

另外,还可以通过 Queue.put_nowait(item) 来添加任务,相当于 Queue.put(item, False),不再赘述。同样,在队列满时,该操作会引发 Full 异常。

相关阅读  Python100天学习计划:第60天:Requests的基本用法

2、获取任务

从队列中获取任务,直接调用 get() 函数即可。

>>> import queue
>>> q = queue.Queue()
>>> q.put(100)
>>> q.get()
100

put() 函数一样,get() 函数也有两个可选参数,完整签名如下 Queue.get(block=True, timeout=None)

默认情况下,当队列空时调用该函数会一直阻塞,直到队列中有任务可获取为止。如果 timeout 是正数,则最多阻塞 timeout 秒,如果这段时间内还没有任务可获取,则会引发 Empty 异常。

>>> import queue
>>> q = queue.Queue()
>>> q.put(100)
>>> q.get()
100
>>> q.get(True,2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "E:\Python37-32\lib\queue.py", line 178, in get
    raise Empty
_queue.Empty # 2 秒钟内没有任务可获取则引发 Empty 异常
>>> q.get() # 该方法会一直阻塞

当 block 为 false 时,timeout 参数将失效。同时如果队列中没有任务可获取则会立刻引发 Empty 异常,否则会直接获取一个任务并返回,不会阻塞。

>>> import queue
>>> q = queue.Queue()
>>> q.put(100)
>>> q.get()
100
>>> q.get(False,2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "E:\Python37-32\lib\queue.py", line 167, in get
    raise Empty
_queue.Empty 
# 指定为非阻塞模式,队列为空则立即引发 Empty 异常

另外,还可以通过 Queue.get_nowait() 来获取任务,相当于 Queue.get(False),不再赘述。同样,在队列为空时,该操作会引发 Empty 异常。

其他常用方法

1、获取队列大小

Queue.qsize() 函数返回队列的大小。注意这个大小不是精确的,qsize() > 0 不保证后续的 get() 不被阻塞,同样 qsize() < maxsize 也不保证 put() 不被阻塞。

>>> import queue
>>> q = queue.Queue()
>>> q.put(100)
>>> q.put(200)
>>> q.qsize()
2

2、判断队列是否空

如果队列为空,返回 True ,否则返回 False 。如果 empty() 返回 True ,不保证后续调用的 put() 不被阻塞。类似的,如果 empty() 返回 False ,也不保证后续调用的 get() 不被阻塞。

相关阅读  Python100天学习计划:第98天:图像库 PIL 实例—验证码去噪

3、判断队列是否满

如果队列是满的返回 True ,否则返回 False 。如果 full() 返回 True 不保证后续调用的 get() 不被阻塞。类似的,如果 full() 返回 False 也不保证后续调用的 put() 不被阻塞。

>>> import queue
>>> q = queue.Queue(maxsize=1)
>>> q.empty()
True
>>> q.full()
False
>>> q.put(100)
>>> q.empty()
False
>>> q.full()
True

队列对比

1、FIFO 队列

queue.Queue() 是 FIFO 队列,出队顺序跟入队顺序是一致的。

import queue
q = queue.Queue()
for index in range(10):
    q.put(index)
while not q.empty():
    print(q.get(), end=", ")
## 输出结果如下
, 1, 2, 3, 4, 5, 6, 7, 8, 9, 

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

3、优先级队列

优先级队列中的任务顺序跟放入时的顺序是无关的,而是按照任务的大小来排序,最小值先被取出。那任务比较大小的规则是怎么样的呢。

3.1 如果是内置类型,比如数值或者字符串,则按照自然顺序来比较排序。

import queue
q = queue.PriorityQueue()
for index in range(10,,-1):
    q.put(index)
while not q.empty():
    print(q.get(), end=", ")
## 输出结果如下
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 

3.2 如果是列表或者元祖,则先比较第一个元素,然后比较第二个,以此类推,直到比较出结果

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

注意,因为列表的比较对规则是按照下标顺序来比较的,所以在没有比较出大小之前 ,队列中所有列表对应下标位置的元素类型要一致。

相关阅读  Python100天学习计划:第63天:正则表达式

好比 [2,1]["1","b"] 因为第一个位置的元素类型不一样,所以是没有办法比较大小的,所以也就放入不了优先级队列。

然而对于 [2,1][1,"b"] 来说即使第二个元素的类型不一致也是可以放入优先级队列的,因为只需要比较第一个位置元素的大小就可以比较出结果了,就不需要比较第二个位置元素的大小了。

但是对于 [2,1] 和 1[2,"b"] 来说,则同样不可以放入优先级队列,因为需要比较第二个位置的元素才可以比较出结果,然而第二个位置的元素类型是不一致的,无法比较大小。

综上,也就是说,直到在比较出结果之前,对应下标位置的元素类型都是需要一致的

3.3 如果是自定义类型,需要实现 __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, 

Queue 总结

本章节介绍了队列以及其常用操作。因为队列默认实现了锁原语,因此在多线程编程中就不需要再考虑多线程安全问题了,对于程序员来说相当友好了。

代码地址

示例代码:https://github.com/JustDoPython/python-100-day/tree/master/day-051



欢迎加入我们的一日成建站教程网QQ学习社群

(关注后回复 1024 获取程序员专属精华资料!




AD:【腾讯云服务器大降价】2核4G 222元/3年 1核2G 38元/年

AD:【腾讯云服务器大降价】2核4G 222元/3年 1核2G 38元/年

AD:【腾讯云服务器大降价】2核4G 222元/3年 1核2G 38元/年

本文来自一日成建站教程网https://www.yiricheng.cn/),为广大编程爱好者提供零基础快速搭建博客网站的方法,分享计算机网络、HTML、CSS、JavaScript、PHP、响应式设计以及Python自动化办公的博客文章,另有各类小工具、电子书、免费教程。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。