什么是队列?(Python队列)

今天来介绍下编程中常会用到的一个数据结构 - 队列。

不知道大家是否还记得什么是数据结构呢?在很早很早以前,Python小课堂的初期,讲了许多 Python 原生的数据结构。比如 list、tuple、dict 等。。。

既然叫数据结构,实际上就是为了给计算机存储数据用的一种结构体。不同的数据结构都有其不同的特点。那今天就来简单地聊聊队列!

队列的概念

抛开计算机知识体系,在咱们的生活中,队列这个词其实挺好想象的,因为无时无刻都可以见到。比如等公交的时候,需要排队。比如买东西交钱的时候,也要排队。在这些例子中,由人们有序形成的队形就叫队列。

生活中的排队,有没有什么特性而言呢?大家可以思考下,再往下看。

普通队列的特性,即先进先出(FIFO,first in first out)。对应到生活中,怎么理解这个先进先出?其实很好理解。

拿排队买东西来说,每次排在队首的人,交完钱肯定是当前队列中第一个离开收银台的人。当队首的人离开了,那么后面的所有人都要往前走,继续结账。

对应到计算机中的队列,就是因为第一个人先排的队,所以他第一个交完钱就可以离开了,即先进先出。(多说句,计算机世界许多东西其实就是抽象的现实世界示例。)

心细的同学会发现,在上面将普通两字标粗了,那一定还有一些其它的常用特殊队列,比如优先级队列。(PriorityQueue)

这次拿去银行办业务来举例。生活中我们去银行办理业务,一般都需要去机器拿号,然后等待着柜台业务人员叫号。叫到你,你就过去处理就行了。但是银行是有 vip 服务的,拥有 vip 权益的人可以更快的享受到业务办理,也就是说人家比你有更高的优先权。vip特权通道,你值得拥有!

了解了生活中的例子,再来看看比较专业的定义:

优先级队列(priority queue) 是0个或多个元素的集合,每个元素都有一个优先权,对优先级队列执行的操作有(1)查找(2)插入一个新元素 (3)删除 一般情况下,查找操作用来搜索优先权最大的元素,删除操作用来删除该元素 。对于优先权相同的元素,可按先进先出次序处理或按任意优先权进行。

百度百科

也就是说,在优先级队列中,每个人都有一个优先权对应,谁的优先权高,谁就会先被处理。大家了解即可。

示例演示

示例情景:

假设下面有 6 个美少女,她们准备去量身高,恰好这几个妹纸是按照从高到低,从大到小排好队的。

什么是队列?(Python队列)

每走一个去量身高,这个队列中就会少一个人。当然,队首在左,队尾在右,于是她们的变化是下面这样:

什么是队列?(Python队列)
什么是队列?(Python队列)

。。。。。。省略,直到:

什么是队列?(Python队列)

代码演示

是不是好多人看到这里就不打算看了。。不是让你来看美少女的喂。。。下面才是重点~

第一部分代码:

import queue
import threading

num_worker_threads = 5

def do_work(beauty_dict):
    print(f"妹纸名字{beauty_dict['name']},年龄{beauty_dict['age']}")

def worker():
    while True:
        item = q.get()
        if item is None:
            break
        do_work(item)
        q.task_done()

简单的讲解下,在 Python3 中,有个模块叫 queue。里面实现了几个队列的数据结构。首先看 do_work() 函数,其中它就是用来打印妹子姓名和年龄的。worker() 函数中写了一层死循环,只要队列中有妹纸的数据,就一直执行打印,直到队列中的妹纸都没了,就跳出。

第二部分代码:

q = queue.Queue()
threads = []
for i in range(num_worker_threads):
    t = threading.Thread(target=worker)
    t.start()
    threads.append(t)

beauty_girls = [{"name": "小H", "age": 23},
                {"name": "小E", "age": 22},
                {"name": "小D", "age": 21},
                {"name": "小C", "age": 20},
                {"name": "小B", "age": 19},
                {"name": "小A", "age": 18},
                ]

for item in beauty_girls:
    q.put(item)

# block until all tasks are done
q.join()

# stop workers
for i in range(num_worker_threads):
    q.put(None)
for t in threads:
    t.join()

首先创建队列,其次,让这 6 个美少女开始依次排入到队列中,开启多线程去执行 worker() 这个函数。worker() 函数就是第一部分代码中,从队列里一个个取出妹纸,在调用 do_work() 打印她们的姓名和年龄。

关于队列的 join() 方法,可以看到官方给的英文注释,大意是当所有任务完成时才继续执行后面的代码,否则处于阻塞状态。

代码中涉及的方法,老规矩,希望大家可以自己去查阅 Python 官方文档,搜索 queue 即可看到。当然如果懒得动手的话,笔者这里截图几个常用方法:

将妹纸放入队列中:

什么是队列?(Python队列)

从队列中获取妹纸:

什么是队列?(Python队列)

获取队列中妹纸的个数:

什么是队列?(Python队列)

总结

通过两篇文章,简单的介绍了一下多线程和队列,目的是为了接下来的多线程队列爬虫示例做准备,如果对这些不了解的话,在后面的代码中是很难看懂的。

关于队列的使用,最常用的方法应该就是放入、获取、判断队列的大小。其实这三点在大部分数据结构里都是常用的操作,熟练掌握即可。