6.15 列表的特殊特性

用列表构建其他数据结构

列表有容器和可变的特性,这使得它非常灵活,用它来构建其他的数据结构不是件难事。我们马上能想到的是堆栈和队列。

1. 堆栈

堆栈是一个后进先出(LIFO)的数据结构,其工作方式就像自助餐厅里面用于放盘子的弹簧支架。把盘子想像成对象,第一个离开堆栈的是你最后放上的那个。在栈上“push”元素是个常用术语,意思是把一个对象添加到堆栈中。反之,要删除一个元素,你可以把它“pop”出堆栈,例6.3展示了一个菜单驱动的程序,它实现了一个简单的、用于存储字符串的堆栈。

逐行解释

1 ~ 3行

一开始是Unix的起始行,然后我们初始化堆栈(其实是个列表)。

例6.3 用列表模拟堆栈(stack.py)

这个简单的脚本把列表作为堆栈用于存储和取回输入的字符串,这个菜单驱动的程序仅使用了列表的append()和pop()方法。

6.15 列表的特殊特性 - 图1

6.15 列表的特殊特性 - 图2

5 ~ 6行

pushit()函数添加一个元素(通过提示由用户输入)到堆栈中。

8 ~ 12行

popit()函数从堆栈中移除一个元素(最新的那个)。试图从一个空的堆栈中移除元素会引发一个错误。这种情况下,用户会得到一个警告提示。当一个元素从堆栈中pop出来时,用户可以看到到底是哪个元素被移除了。我们用反单引号(`)来代替repr()函数,把字符串的内容用引号括起来显示,而不是单单显示字符串的内容。

14 ~ 15行

viewstack()方法显示堆栈现有的内容。

17行

虽然我们下一章才会正式讲解字典类型,但是这里我们还是希望给你展示一个小例子,一个包含命令的矢量(CMD)。这个字典的内容是前面定义的三个“动作”函数,它们可以通过字母进行访问,用户必须输入这些字母来执行相应的命令。比如说,要进栈一个字符串,用户就必须输入‘u’,那么字母‘u’是如何从字典里面访问到pushit()函数的呢?在第43行执行了选择的函数。

19 ~ 43行

整个菜单驱动的应用都是由showmenu()函数控制的。它首先向用户提供一个选单,如果用户输入了合法选项就调用相应的函数。我们还没有详细地涉及到异常的处理,try-except语句,但本节里面的代码允许用户输入^D (EOF,产生一个EOF错误)或者^C(中断退出,产生一个KeyboardInterrupt异常),这两种操作在我们的脚本里面都会得到处理,结果等同于用户输入‘q’退出应用程序。这是对Python异常处理特性的一次应用,说明了Python的异常处理机制是多么方便。外循环用来执行用户输入的指令直到用户退出应用,内循环提示用户输入一个合法的命令项。

45 ~ 46行

如果调用文件,这部分的代码就会启动程序。如果该脚本只是被作为一个模块导入,则仅仅是导入定义的函数和变量,而菜单也就不会显示。关于第45行和_name_变量,请查阅第3.4.1节。

下面简单的执行了一下该脚本。

6.15 列表的特殊特性 - 图3

6.15 列表的特殊特性 - 图4

2. 队列

队列是一种先进先出(FIFO)的数据类型,它的工作原理类似于超市中排队交钱或者银行里面的排队,队列里的第一个人首先接受服务(满心想第一个出去)。新的元素通过“入队”的方式添加进队列的末尾,“出队”就是从队列的头部删除。下面的例子里面展示了这种操作,我们把上面的堆栈的例子进行了改造,用列表实现了一个简单的队列。

例6.4 把列表用做队列(queue.py)

这个例子中,我们把列表用做队列来存储和取回菜单驱动应用里面输入的字符串,只用到了列表的append()和pop()方法。

6.15 列表的特殊特性 - 图5

6.15 列表的特殊特性 - 图6

逐行解释

该脚本跟上面的stack.py非常相似,所以我们只讲解一下有显著不同的行。

1 ~ 7行

定义了几个后面脚本要用到的常量。

5 ~ 6行

enQ()方法跟pushit()方法非常相近,只不过名字改变了。

8 ~ 12行

两个脚本的主要差别就在于此,deQ()函数不像popit()函数那样把列表的最后一个元素弹出来,而是第一个元素。

17、21 ~ 24、36行

选项改变了,所以我们也需要重写原来的提示信息和输入检查。

还是在这里列举一些输出。

6.15 列表的特殊特性 - 图7