探索Python,第4部分:探索Python类型的层次结构

转载:http://www-128.ibm.com/developerworks/cn/opensource/os-python4/
使用列表
级别:初级
Robert Brunner
(
[email=rb@ncsa.uiuc.edu?subject=%E6%8E%A2%E7%B4%A2%20Python%20%E7%B1%BB%E5%9E%8B%E7%9A%84%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84&cc=rb@ncsa.uiuc.edu]rb@ncsa.uiuc.edu[/email]
), NCSA 研究专家,天文学助理教授, University of Illinois, Urbana-Champaign
2005 年  8 月  30 日
    Python 提供了一系列有用的功能,其中list类是最重要的功能之一。本文介绍list类,并演示了众多方法中的一些方法,了解如何使用这些方法简化困难的编程任务。
    本系列的第二篇文章“探索Python,第2部分:探索Python类型的层次结构——了解对象和容器“介绍了 Python类型的层次结构,其中包括容器对象,这篇文章演示了tuple,这是一个不可变的序列。本系列的第三篇文章“探索Python类型的层次结构——使用字符串“介绍了Python string,这也是一个不可变的序列,但仅针对字符数据。作为不可变的序列,tuple和string对象在创建之后便无法修改。如果需要修改其中的一个,则必须创建相应类型的新容器,以便容纳新数据。本文介绍一个新的序列类型:list,这是一个可变的序列类型,并演示以多种不同方法使用它。
Python list
    在介绍Python tuple时,我使用了类比的方法,将其比做一个袋子,您可以在袋子中存放不同的东西。Python list与此非常类似,因此,它的功能与袋子的功能也非常类似。但有一点是不同的,即您可以使用方括号创建list,如清单1所示。
清单1. 在Python中创建一个list
>>> l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> type(l)
>>> el = []    # Create an empty list
>>> len(el)
0
>>> sl = [1]    # Create a single item list
>>> len(sl)
1
>>> sl = [1,]    # Create a single item list, as with a tuple
>>> len(sl)
1
    本例展示如何创建包含从0到9(包括0和9)的简单list,以及如何创建一个空列表和一个包含单个条目的列表。如果您还记得的话,创建单个条目的tuple还需要在单个条目后面跟一个逗号。这是区分单个条目 tuple与方法调用的必要条件,这一点将在以后的文章中详细讨论。而对于list,则是不必要的,尽管也允许使用单个逗号。
    与往常一样,要获取有关Python主题的更多信息,您可以使用内置的帮助解释器,例如,清单2展示了如何开始list类的帮助描述。
清单2. 获取有关list的帮助
>>> help(list)
Help on class list in module __builtin__:
class list(object)
|  list() -> new list
|  list(sequence) -> new list initialized from sequence
    如果仔细观察清单2中对list类的描述,您会看到其中提供了两个不同的构造函数:一个没有参数,另一个接受一个序列类作为参数。因此,使用构造函数及方括号简化符号,可以创建list。这就提供了很大的灵活性,原因是您可以方便地将现有的序列,如tuple或string转换为list,如清单3所示。不过,请注意,传递的参数必须是序列——并且不只是对象序列——否则将会出现错误。对于任何序列类型,您都可以使用len方法容易地查找序列中条目的数量。
清单3. 直接创建list对象
>>> l = list()
>>> type(l)
>>> len(l)
0
>>> l
[]
>>> l = list((0, 1, 2, 3, 4, 5, 6, 7, 8, 9))    # Create a list from a tuple
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> len(l)
10
>>> l = list([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])    # Create a list from a list
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> len(l)
10
>>> l = list(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)      # Error: Must pass in a sequence
Traceback (most recent call last):
  File "", line 1, in ?
TypeError: list() takes at most 1 argument (10 given)
>>> l = list("0123456789") # Create a list from a string
>>> l
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
>>> type(l)
>>> len(l)
10
    正如您看到的,创建list是很容易的,如果还没有尝试过,现在可以试一试。您不仅能够将序列直接传递给构造函数,还可以将拥有元组或字符串的变量传递给list构造函数。
    很明显,序列较为有用的主要原因是它可以非常方便地访问序列中的条目。如果还记得对tuple的讨论,便知道可以在序列中一次访问一个条目或者通过将条目切片来访问条目。Python list也可以使用相同的技术,如清单4所示。
清单4. 从list访问条目
>>> l = list([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> l[0]         # Get the first item in the list
0
>>> type(l[0])
>>> l[5]         # Get the sixth item in the list
5
>>> l[1:5]       # Get the second through fifth items
[1, 2, 3, 4]
>>> type(l[1:5])
>>> l[0::2]      # Get every second item
[0, 2, 4, 6, 8]
>>> l[0], l[1], l[2]   
(0, 1, 2)
    在以前的文章中已经了解到,切片是一个非常有用的概念,其一般形式为l[start:end:step],其中 start和end分别是开始和结束索引,step是在切片时要跨过的条目数量。此外,还可以对结束索引使用负值,即从序列的结尾往回计数。另一个有用的功能是以一种很合适的方式处理错误(如超过序列的长度)。如前一个例子所示,您还可以选择忽略切片中使用的三个值中的一个或多个值。例如,我在切片 l[0::2] 中没有使用结束索引。
可变的序列
    在本文的开头,我提到过list和tuple之间的主要区别在于list是一个可变的序列,这就意味着您不但可以方便地访问list中的条目,而且可以方便地修改它们。但这会引起一个并发症状:您只能修改序列中的条目。若要向序列中添加条目(而不仅仅是修改条目),可使用append方法,如清单5所示。
清单5. 修改list
>>> l = []
>>> l[0] = 0      # The list is empty
Traceback (most recent call last):
  File "", line 1, in ?
IndexError: list assignment index out of range
>>> l.append(0)
>>> l
[0]
>>> l[0] = 1
>>> l
[1]
    正如前一个例子所演示的,尝试修改不存在的list条目会导致出现错误。这一点意义重大,并演示了 Python方法生成错误的情况。当问题较为严重时,将会产生一个错误,如果问题较小并且可以很容易地处理,则忽略它。
异构的可变序列
    您可能想了解更为复杂的修改。通过综合切片知识以及如何修改list的知识,您应该已经获得了非常重要的见识:可以通过多种方式修改列表。就像tuple一样,list 也可以持有不同类型的数据(或不同类型的对象),这就是我所说的异构的可变序列。这两种功能在清单6中进行了更完整的描述。

清单6. 异构的可变list
>>> l=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> l[2] = 2
>>> type(l[2])
>>> l[2] = "two"      # Change the type of an element
>>> type(l[2])
>>> l
[0, 1, 'two', 3, 4, 5, 6, 7, 8, 9]
>>> l[2] = l[2:5] * 2
>>> l
[0, 1, ['two', 3, 4, 'two', 3, 4], 3, 4, 5, 6, 7, 8, 9]
>>> del(l[2])         # Remove single element
>>> l
[0, 1, 3, 4, 5, 6, 7, 8, 9]
>>> l[1:3] = []       # Remove a slice
>>> l
[0, 4, 5, 6, 7, 8, 9]
    修改list中的条目相当容易:您可以适当地设置条目的值,甚至设置成另一种不同的类型,如string 或另一list。您还可以使用重复运算符,可以将该运算符识别为乘法运算符,以便从小片段中构建更大的列表。
    前面的例子向您展示了如何向list中添加元素,以及如何修改list中的条目。前一个例子还演示了如何从list中删除对象。删除条目的第一个方法是使用del方法。使用此方法可以删除一个条目或一个条目范围。您还可以使用灵活而强大的切片方法从list中删除切片。
数组
    在前一个例子中您可以看到,list可以包含另一个list作为条目。如果扩展此例子,您可能想知道每个条目由一个list替换将会发生什么样的事情。结果是一个数组,或者从更加数学方面来讲是一个矩阵。清单7 展示了如何使用list保持二维(2-D)或三维(3-D)数组。
清单7. list作为一个数组
>>> al = [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
>>> al
[[0, 1, 2], [3, 4, 5], [6, 7, 8]]
>>> al[0][0]          # First element in 2D array
0
>>> al[2][2]          # Last element in 2D array
8
>>> al[1][2]
5
>>> al = [[[0, 1], [2, 3]], [[4, 5], [6, 7]]]
>>> al
[[[0, 1], [2, 3]], [[4, 5], [6, 7]]]
>>> al[0][0][1]
1
>>> len(al)           # Length of outer dimension
2
>>> len(al[0])        # Length of middle dimension
2
>>> len(al[0][0])     # Length of inner dimension
2
其他列表操作
    list对象具有许多可以应用于现有列表的有用方法。例如,您可以反转list中的所有条目或排序 list。不过,要记住这些操作的一个重点在于,它们是就地操作,这意味着它们会修改调用它们所针对的 list。因此,如果您尝试创建新列表,并将其设置为对这些方法之一调用所产生的结果,则会得到一个空列表。
    list除可以用于模拟数组外,还可以用于模拟其他数据结构。例如,append和pop方法对list函数的操作要么是先进先出 (FIFO) 数据结构(也称为队列),要么是后进先出 (LIFO) 数据结构(也称为堆栈)。通过允许您将条目设置为从list中弹出(删除并返回),pop方法支持这些功能。如果弹出list的第一项,则是一个队列;反之,如果弹出list的最后一项,则是一个堆栈,如清单8所示。
清单8. 操纵list
>>> l=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> id(l) # This is the object id for our current list
4525432
>>> l.reverse()       # Reverse the list
>>> l
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> id(l) # The id is the same, modified list in place.
4525432
>>> l.sort()          # Sort the list in numerical order
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> id(l) # Modified the existing list
4525432
>>> l.index(5)        # Same as l[5]
5
>>> l.count(0)        # How
many times does '0' occur in the list
1
>>> l.pop()           # Take off the last item (Stack)
9
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8]
>>> l.pop(5)          # Take out the fifth element
5
>>> l
[0, 1, 2, 3, 4, 6, 7, 8]
>>> l.pop(0)          # Take the first item off the list (Queue)
0
>>> l
[1, 2, 3, 4, 6, 7, 8]
列表:切片和切块
    本文介绍了list,它是一个容器对象,可以方便地进行修改,而且可以持有不同类型的数据。由于它具有相当的灵活性,因此list是Python编程语言中最常用的结构之一已不足为怪。list像一个口袋,可以容纳不同类型的数据,并可以根据需要更改。您可以像使用数组一样使用list,以有组织的方式容纳数据;您还可以像使用队列或堆栈一样使用list。在以后的文章中还将更为深入地探索这一灵活性,并介绍强大的编程技术,即列表理解。
参考资料
学习
  • 您可以参阅本文在 developerWorks 全球站点上的
    英文原文


  • 本系列文章从
    探索 Python,第 1 部分:Python 的内置数值类型
    开始。

  • 探索 Python,第 2 部分:探索 Python 类型的层次结构 —— 了解对象和容器
    讨论了该语言的对象特性 —— 最初用于构建内置的简单类型,此外还介绍了 Python tuple 类,并使用该类演示了容器类型的概念。

  • 探索 Python,第 3 部分:探索 Python 类型的层次结构 —— 使用字符串
    介绍了字符串类,并演示了在 Python 中使用字符串的不同方式。

  • IBM developerWorks 发表了许多关于 Python 的文章,包括 David Mertz 撰写的高级别的
    可爱的 Python


  • 如果您更喜欢使用 IDE,请阅读 developerWorks 文章
    用 Eclipse 和 Ant 进行 Python 开发
    ,作者是 Ron Smith,介绍了如何使用 Eclipse 编写 Python 代码。

  • Python 参考手册讨论了
    Python 的对象特性


  • 如果您拥有正常使用的 Python 解释器,则
    Python 教程
    是一个开始学习该语言的很好去处。

  • 访问 developerWorks 的
    开放源码专区
    ,获得广泛的 how-to 信息、工具和项目更新,从而帮助您使用开放源码技术进行开发并将它们用于 IBM 的产品中。
获得产品和技术

  • 下载 Python


  • 从 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 获取评估产品,并开始在 IBM 中间件上构建应用程序和部署它们。选择 Linux® 或 Windows® 版本的免费
    软件评估工具 (SEK)


  • 使用
    IBM 试用软件
    改革您的下一个开放源码开发项目,可以通过下载,也可以从 DVD 安装。
讨论

关于作者
    Robert J. Brunner 是美国国家超级计算应用中心的研究专家,而且是伊利诺伊大学香槟分校的天文学助理教授。他已出版了好几本书,撰写了许多文章和教程,涉及的主题相当广泛。您可以通过 rb@ncsa.uiuc.edu 与他联系。