容器数据类型
Python提供的容器数据类型可以高效地处理复杂的操作,同时十分易于使用。
列表
列表(list)是一种用于存储元素序列的容器类型。跟字符串不同的是,列表是可变类型——可以在运行时修改它们。可以通过一系列例子来描述列表数据类型,这也是最好的方式。
这段代码展示了如何用方括号创建一个列表,以及如何用三个整数元素填充它。你还会发现列表中可以存在重复元素。len()函数返回的是列表中的元素数量。
关键词:is
关键字is的作用是检查两个变量是否指向内存中的同一个对象,这可能会让Python新手感到困惑。清单1-7的代码检查了两个整数和两个列表是否在内存中指向同一个对象。
清单1-7:使用关键字is
如果你创建了两个列表——即使它们包含的元素是相同的——它们仍然会指向内存中两个不同的列表对象。修改其中一个列表并不会影响另一个列表。之所以说列表是可变的,正是因为你可以在创建列表之后对其进行修改。因此,如果你去检查它们在内存中是否指向相同的对象,结果会是False。不过,整数是不可变类型,所以不存在一个变量修改了其指向的对象,而影响到其他所有指向这个对象的变量的风险。原因是整数对象3就是不可修改的。试图修改的话只会创建出一个新的整数对象,旧的对象是不会改变的。
添加元素
Python提供了三种常见方法来为现有的列表添加元素:追加(append)、插入(insert)和列表连接。
这三种操作都会生成同样的列表[1,2,3,4],不过追加操作是最快的,因为它既不需要遍历列表以在正确的位置插入元素(insert就是这么做的),也不需要基于两个子列表去创建一个新列表(列表连接)。因此可以简单认为,只有想在列表非末尾的某个特定位置插入元素的时候,才需要用到insert。列表连接则可以用来连接两个任意长度的列表。注意还有第四种方式,extend(),它提供了一种快速的方式,一次把多个元素追加到指定列表中。
移除元素
通过使用remove(x)方法,可以很容易地从列表里删除一个元素x。
该方法操作的是列表对象本身,而不是根据所做修改创建一个新的列表。在前面的代码示例中,我们创建了一个叫作l的列表对象,并通过删除一个元素的操作,在内存中直接修改了这个对象。这样可以避免为同样的列表数据创建冗余的副本,从而节约内存开销。
反转列表
可以使用list.reverse()方法来颠倒列表元素的顺序。
反转列表也会修改原来的列表对象,而不是去创建一个新的列表对象。
列表排序
使用list.sort()方法,可以给列表排序:
同样,列表排序也会修改原始列表对象,得到的结果是以升序方式排序的。包含字符串对象的列表会以字典升序的方式排序(从a到z)。排序函数一般会假设两个对象是可比较的,粗略来说,如果对于任意数据类型a和b,可以计算a>b,Python就能对列表[a,b]进行排序。
列表元素的索引
可以使用list.index(x)方法,找出一个特定元素x在列表中的索引。
index(x) 方法会找出元素x 在列表中第一次出现的位置,并返回对应的索引。像其他主要的编程语言一样,Python为第一个位置分配的索引是0,为第i 个位置分配的索引是i-1。
堆栈
直观地讲,堆栈数据结构是以先进先出(FIFO)方式工作的。把它想象成一叠文件:把每份新文件放在现有的那叠文件上面,而当你在堆栈上工作的时候,每次都会从最顶上取出文件。在计算机科学中,堆栈依然是一种基本的数据结构,用于操作系统管理、算法、语法解析和回溯。
Python列表可以直观地作为堆栈使用,列表操作方法append()可以用于向栈中添加项目,pop()则可以弹出最近添加的项目。
由于列表的实现极有效率,通常没有必要引入外部堆栈库。
集合
集合数据类型是Python和其他众多编程语言的一种基本数据类型。流行的分布式计算语言(比如MapReduce或Apache Spark)甚至几乎只关注集合操作,作为其编程基元。所以到底什么是集合?集合(set)就是一组无序且唯一的元素。把这个定义的几个主要方面拆开来看一下。
一组元素
集合是类似列表或者元组的一组元素,集合可以由基本元素(整数、浮点数、字符串等)或者复杂元素(对象实例、元组)组成。不过,集合中所有的数据类型必须是可哈希的,也就是说每个元素必须有一个对应的哈希值。一个对象的哈希值会用于把这个对象跟其他对象进行比较,因此哈希值必须是永远不变的。我们看一下清单1-8,它检查了三个字符串的哈希值后,用这些字符串创建了一个集合。如果试图创建一些列表的集合,就会失败,因为列表是不可哈希的。
清单1-8:集合数据类型只允许包含可哈希的元素
可以创建字符串的集合,因为字符串是可哈希的。但是不能创建列表的集合,因为列表是不可哈希的。其原因是哈希值取决于元素的内容,但列表是可变的。如果你改变了列表数据,哈希值就也得变。由于可变数据类型都是不可哈希的,你也就不能把它们放到集合里。
无序
与列表不同,集合中的元素没有固定的顺序。不论以什么顺序把它们放到集合中,你永远也无法确定集合以什么顺序来存储这些元素。下面是一个例子:
先输入的是hero,但是解释器先打出的是enemy(显然,Python解释器是黑暗一方的)。注意,你的解析器可能会以另一种顺序打出这些集合元素。
唯一
集合中的所有元素都是唯一的。严格地说:集合中满足x!=y的两个值x,y,一定具有不同的哈希值,即hash(x)!=hash(y)。因为集合中的任意两个元素都是不同的,你也就不能创建一支由哈利·波特的克隆人组成的大军去对抗伏地魔:
无论你多少次把同样的值放入同一个集合中,集合只会存储这个值的一个实例,原因是这些英雄具有同样的哈希值,而每一个集合对同一个哈希值的元素只会包含一次。普通集合类型有一种扩展的类型,叫作多重集合(multiset),这种数据结构可以存储同一个值的多个实例,然而在实践中很少用到。相反,你会在几乎任何一个有意义的代码库中发现集合的运用——例如,对一组顾客的集合与一组来过某家商店的人计算交集,而得到一个新的集合,其中每个顾客都来过这家商店。
字典
字典是一种很有用的数据结构,用来存储键值对(key,value)。
可以通过指定方括号里的键名来读写对应的值:
使用keys()和values()函数,可以获取字典中所有的键和值。
使用items()方法,可以以(key,value)元组的形式获取字典里的所有数据:
用这种方式,可以轻松地迭代遍历一个字典中的所有键与值,而不用去单独访问它们。
成员
使用关键字in,可以检查一个值是否在字典、列表或集合中(见清单1-9)。
清单1-9:使用关键字in
上面使用关键字in来检测整数值42是否在一个整数列表中➊,以及字符串“21”是否在一个字符串列表中➋。如果x出现在y中,我们就说x是y的一个成员。
检测是否为集合的成员,比检测是否为列表的成员更快:为了检查一个元素x是否出现在列表y中,需要遍历整个列表,直到找到x或者全部遍历完全部元素。然而,集合的实现很像字典:要检测元素x是否出现在集合y中,Python内部只需要执行一个操作y[hash(x)],并且检查返回值是不是None就知道了。
列表和字典解析
列表解析是一项很受欢迎的Python特性,用于帮你快速创建和修改列表。形式可以简单写为[ 表达式+ 上下文]:
表达式 告诉Python如何处理列表中的每个元素。
上下文 告诉Python选择哪些元素。
例如,在列表解析语句[x for x in range(3)]中,第一个部分x就是(恒等的)表达式,第二部分for x in range(3)是上下文,这句代码生成了列表[1,2,3]。示例中传给range()函数的参数会让它返回指定范围的整数值0、1和2。关于列表解析的另一个例子如下所示:
集合解析也跟列表解析类似,但创建的是一个集合而不是列表。