关于Python队列的实例教程

151 阅读8分钟

队列是一种线性抽象数据类型,与堆栈有一些关键区别。队列按照项目的添加顺序保存项目的集合。被添加到队列后面的项目和从队列前面删除的项目。你经常会遇到排队的情况。你上次去买菜的时候,你很可能要排队结账。如果你把排队看成是顾客的队伍,顾客把自己加到队伍的末端,最终从队伍的前面离开。这被称为先进先出或FIFO。这与堆栈不同,后者是后进先出。此外,队列也保留了秩序,所以当你考虑到人们加入队伍时,秩序也被保留下来。现在让我们看一下 Python 中队列的例子。

一个队列类

对于这个 Python 中的队列的例子,我们将使用一个类,它有实现队列功能所需的所有方法。这个类将被简单地命名为Queue,我们将用一个列表来表示我们的队列。在 __init__ 方法中,我们可以将项目初始化为一个空列表。为了设置将东西送入和送出队列所需的基本功能,我们可以存根出一个enqueue()方法用于添加,一个dequeue()方法用于删除。enqueue()方法需要一个item的参数,这样我们就可以把它添加到队列中。dequeue()方法不需要item参数,因为我们总是要从列表的末端弹出,它会自动为我们取出列表的最后一个项目。要检查队列中下一个要被删除的项目,我们可以使用peek()方法。我们还希望能用size()检查队列的大小,用is_empty()检查队列是否为空。

class Queue:
    def __init__(self):
        self.items = []

    def enqueue(self, item):
        pass

    def dequeue(self):
        pass

    def size(self):
        pass

    def peek(self):
        pass

    def is_empty(self):
        pass

enqueue()

现在我们可以开始编码我们的队列类所需要的方法了。我们将从enqueue()方法开始。对于这个方法,我们必须把我们想要添加到队列中的项目作为一个参数传入。在我们方法的主体中,我们要以一种类似队列的方式将这个项目插入到列表中。在队列中,我们不会像在堆栈中那样,将项目追加到列表的末尾。我们要把这个项目插入到列表的第2个索引,或者换句话说,就是列表的第一个位置。我们这样做的原因是,我们想把列表的末尾留作弹出项目,而用列表的前面来插入。

class Queue:
    def __init__(self):
        self.items = []

    def enqueue(self, item):
        self.items.insert(0, item)

    def dequeue(self):
        pass

    def size(self):
        pass

    def peek(self):
        pass

    def is_empty(self):
        pass

我们可以回到终端,在交互式 Python shell 中测试一下。我们首先创建一个 Queue 对象,然后向其中排队。首先,我们把'先排到了'这个字符串放到队列中。这将是队列中要处理的第一个项目。然后我们添加另一个字符串'排在第二'。最后,我们添加'排在最后'的字符串。现在我们可以检查这些项目的内容,我们可以看到它们的顺序是正确的。如果这是在商店里排队,右边的物品将被首先处理。

dequeue()

现在让我们创建**dequeue()**方法,它将帮助我们把项目从队列中取出。队列的前面实际上是列表的末端。既然如此,我们可以使用列表内置的pop()方法,我们将总是得到队列中最前面的项目,因为pop方法总是返回列表中的最后一个项目。然而,在你试图从队列中取出项目之前,你应该确保有一些项目要从队列中处理。如果你试图在一个空的队列中弹出,你会得到一个错误。

class Queue:
    def __init__(self):
        self.items = []

    def enqueue(self, item):
        self.items.insert(0, item)

    def dequeue(self):
        if self.items:
            return self.items.pop()
        else:
            return None

    def size(self):
        pass

    def peek(self):
        pass

    def is_empty(self):
        pass

我们将再次在交互式 Python 解释器中测试这个问题。我们创建一个新的队列,然后向它添加一些项目。这些是 "第一"、"第二 "和 "第三 "字符串。我们确认这些项目现在都在队列中,而且是按照我们期望的顺序。现在一旦我们开始调用dequeue()方法,最右边的,或者说第一个项目应该被处理。我们看到情况就是这样,当继续调用dequeue()方法时,队列中的项目被处理,就像杂货店里的排队一样。

size()

Queue类的size()方法其实很简单。它所需要做的就是返回队列列表中的项目的长度。

class Queue:
    def __init__(self):
        self.items = []

    def enqueue(self, item):
        self.items.insert(0, item)

    def dequeue(self):
        if self.items:
            return self.items.pop()
        else:
            return None

    def size(self):
        return len(self.items)

    def peek(self):
        pass

    def is_empty(self):
        pass

测试size()方法显示它的工作是正确的。

peek()

当我们只是想看看队列中下一个要处理的项目是什么时,就可以使用这个方法。需要做的就是返回列表中的最后一个项目,因为列表中的最后一个项目就是接下来要被删除的项目。为了确保你不会得到一个列表索引超出范围的错误,你要在偷看之前检查是否有项目可以偷看。我们可以在代码中处理这种情况,说只要列表中还有项目,就向我们显示列表中的最后一个项目。这有点像dequeue()的工作原理,但不同的是peek()使队列保持完整,而dequeue()在被调用时实际上是在处理或删除一个项目。

class Queue:
    def __init__(self):
        self.items = []

    def enqueue(self, item):
        self.items.insert(0, item)

    def dequeue(self):
        if self.items:
            return self.items.pop()
        else:
            return None

    def size(self):
        return len(self.items)

    def peek(self):
        if self.items:
            return self.items[-1]
        else:
            return None

    def is_empty(self):
        pass

is_empty()

我们要看的最后一个方法是确定队列是否是空的。这是通过简单地返回self.items == []的平等检查来完成的。换句话说,如果 items 等于一个空列表,那么就返回 True。如果 items 不等于一个空列表,则返回 False。

class Queue:
    def __init__(self):
        self.items = []

    def enqueue(self, item):
        self.items.insert(0, item)

    def dequeue(self):
        if self.items:
            return self.items.pop()
        else:
            return None

    def size(self):
        return len(self.items)

    def peek(self):
        if self.items:
            return self.items[-1]
        else:
            return None

    def is_empty(self):
        return self.items == []

打印队列的例子

有了关于队列工作原理的新知识,让我们看看一个可能遇到队列的更真实的场景。想一想,当你向一台共享打印机打印文件时。如果有打印作业排在你的前面,在你的打印作业被处理之前可能需要一点时间。这就是队列的工作方式。你必须在最后排队,等待你前面的人被处理。下面的代码显示了如何使用三个名为 PrintJob、PrintQueue 和 Printer 的 Python 类来实现一个打印队列。PrintQueue 类使用了熟悉的队列方法 enqueue()、dequeue() 和 is_empty()。对于PrintJob类,有一些逻辑来决定还剩下多少页要打印。在这个例子中,我们最多只能打印5页。如果有一页被打印出来,那么页数就会被递减,一旦没有更多的页数,那么打印就结束了。打印机类有逻辑来理解它当前正在处理的PrintJob。这些类相互作用,因为Printer类能够通过从非空的PrintQueue对象中去排队来获得下一个作业。

import random


class PrintQueue:

    def __init__(self):
        self.items = []

    def __str__(self):
        pass

    def enqueue(self, item):
        self.items.insert(0, item)

    def dequeue(self):
        return self.items.pop()

    def is_empty(self):
        return self.items == []


class PrintJob:

    def __init__(self):
        self.pages = random.randint(1, 6)

    def __str__(self):
        return f'PrintJob({self.pages})'

    def print_page(self):
        if self.pages > 0:
            self.pages -= 1

    def check_complete(self):
        if self.pages == 0:
            return True
        return False


class Printer:

    def __init__(self):
        self.current_job = None

    def get_job(self, print_queue):
        try:
            self.current_job = print_queue.dequeue()
        except IndexError:
            return "No more jobs to print."

    def print_job(self, job):
        while job.pages > 0:
            job.print_page()

        if job.check_complete():
            return "Printing complete."
        else:
            return "An error occurred."

下面是实际操作中的代码。我们设置了两个j1和j2的打印作业。然后我们实例化一个打印队列。利用这个队列,我们将这两个作业排入打印队列。现在我们通过Printer()类设置了一个打印机对象。为了得到第一个要打印的作业,我们调用print.get_job()方法,同时传入队列本身。从那里,我们使用 printer.print_job() 方法来打印当前作业,同时传入当前作业。我们可以看到,这第一个作业只有1页,并被成功打印出来。接下来要做的是抓取队列中的下一个作业并将其打印出来。我们看到有5页被打印出来了。最后,我们试图从队列中获取更多的作业,但是我们看到,由于我们已经清空了队列,没有更多的作业可以打印。

Python 队列实例总结

在本教程中,我们了解了队列,它是一种抽象的数据类型或线性数据结构。我们看到了如何使用Python类来建立一个队列,然后如何使用一个打印实例来实现一个队列的真实场景。队列在计算机科学中的其他用途包括在单一共享资源上为请求提供服务,如CPU任务,或呼叫中心软件应用程序使用队列来按顺序扣留给他们打电话的人,直到服务代表有空。你当然已经听到了这个消息,你的电话将按照收到的顺序被回答。另一个例子可能是实时系统中对中断的处理。中断是按照它们到达的顺序来处理的。单行道也是一个队列的例子,第一个进入单行道的车也是第一个离开单行道的车。你明白了吧,但最后一个排队的例子是一个自动洗车楼。第一辆进入大楼并被清洗的汽车也是第一辆离开大楼的汽车,干净而光亮。