2.1 序列概览
数据结构是通过某种方式(例如对元素进行编号)组织在一起的数据元素的结合,这些数据元素可以是数字或者字符或其他数据结构。最基本的数据结构是序列(sequence),序列中的每个元素被分配一个序号----即元素的位置,也称为索引。第一个索引是0,第二个则是1,以此类推。
Python包含6中内建的序列,最常用的两种类型是:列表和元组。其他的内建序列类型有字符串、Unicode字符串、buffer对象和xrange对象。
列表和元组的主要区别在于,列表可以修改,元组则不能。
列表形式用如下方式表示:
1 >>> edward = ["Edward Gumby",42] 2 >>>john = ["John Smith",50] 3 >>>database = [edward,john] 4 >>>database 5 [["Edward Gumby",42],["John Smith",50]]
2.2 通用序列操作
通用序列操作是指对所有序列类型可以进行的特定操作,也就是说可以对常用的列表和元组进行:索引(indexing)、分片(sliceing)、加(adding)、乘(multiplying)以及检查某个元素是否属于序列的成员(成员资格)。除此之外,Python还有计算序列长度、找出最大元素和最小元素的内建函数。还有一个重要的操作迭代(iteration):依次对序列中的每个元素重复执行某些操作。这个会后续介绍。
2.2.1 索引
序列中的所有元素都是有编号的即索引----从0开始递增。这些元素都是通过索引进行访问获取。正数索引会从左到右开始计数(0开始),负数索引会从右到左计数(最后一个元素的索引为-1)。
>>>greeting = "Hello" >>>greeting[0] 'H' >>>greeting[-1] 'o' >>>"Hello"[0] 'H'
如果一个函数调用返回一个序列,那么可以直接对返回结果进行索引操作。例如,假设你只对用户输入年份的第4个数字感兴趣,那么,可以进行如下操作:
>>> fourth = input("Year:")[3] Year:2005 >>> fourth '5'
代码清单2-1是一个示例程序,它要求输入年,月(1~12的数字)、日(1~31),然后打印出相应日期的月份名称,等等。
#根据给定的数字形式的年月日打印出日期 months = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ] #以1~31的数字作为结尾的列表 endings = ["st","nd","rd"] + 17 * ["th"] + ["st","nd","rd"] + 7 * ["th"] + ["st"] year = input("Year: ") month = input("Month (1-12): ") day = input("Day (1-31): ") month_number = int(month) day_number = int(day) #将月份和天数减1,以获得正确的索引 month_name = months[month_number-1] day_name = day + endings[day_number-1] print(month_name,"",day_name,",",year)
以下是程序执行的一部分结果:
Year: 1984
Month (1-12): 3
Day (1-31): 27
March 27th , 1984
2.2.2 分片
与使用索引来访问某个元素类似,可以使用分片操作来访问一定范围内的元素。分片通过冒号相隔的两个索引来实现。第一个索引是需要提取部分的第一个元素的标号,而最后的索引则是分片之后剩下部分的第一个元素的标号。
>>> number = [1,2,3,4,5,6,7,8,9,10] >>> number[3:6] [4, 5, 6]
简而言之,分片操作的实现需要提供两个索引作为边界,第一个索引的元素是包含在分片内的,而第2个则不包含在分片内(索引6不含在分片内,所以就取到索引5对应的元素6)。总结五个字就是"顾首不顾尾”。
1.捷径
>>> number[7:10] [8, 9, 10] >>> number[7:] [8, 9, 10] >>> number[-3:] [8, 9, 10]
如果要取出序列中包括序列开始或者结尾的元素,只需置空第一个索引或者最后一个索引即可。如果需要复制整个序列,可以将两个所以都置空:
>>> number[:3] [1, 2, 3] >>> number[:] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
2.步长
步长(step length)通常都是隐式设置的,在普通的分片中,步长是1----分片操作就是按照这个步长遍历序列的元素,然后返回开始和结束索引点之间的所有元素。
>>> number[0:10:1]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
如果步长被设置为比1大的数,那么就会跳过某些元素。例如,步长为2的分片包括的是从开始到结束每隔1个的元素。如果需要将每4个元素中的第1个提取出来,只要将步长设置为4即可:
>>> number[0:10:2] [1, 3, 5, 7, 9] >>> number[3:6:3] [4] >>> number[::4] [1, 5, 9]
步长可以是负数,即从右到左提取元素:
>>> number[8:3:-1] [9, 8, 7, 6, 5] >>> number[10:0:-2] [10, 8, 6, 4, 2] >>> number[0:10:-2] [] >>> number[::-2] [10, 8, 6, 4, 2] >>> number[5::-2] [6, 4, 2] >>> number[:5:-2] [10, 8]
总结:
起始索引对应的元素(最左边被索引的元素)包括在结果之中,而结束点索引对应的元素(最右边)则不在分片之内。对于一个正数步长,不管索引是从左到右(0:9)还是从右到左(-1:-10),起始索引一定小于结束索引,Python会从序列的头部开始向右提取元素排列出来,直到最后一个元素;同样对于负数步长,则是从序列的尾部开始向左提取元素,直到第一个元素。不管索引是从左到右(0:9)还是从右到左(-1:-10),起始索引(开始点)一定大于结束索引(结束点),否则不会输出分片结果。
2.2.3 序列相加
通过使用加号可以进行序列的连接操作:
>>> [1,2,3] + [4,5,6] [1, 2, 3, 4, 5, 6] >>> "Hello," + "World!" 'Hello,World!' >>> [1,2,3] + "Hello," Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: can only concatenate list (not "str") to list
正如错误信息所提示的,列表和字符串是无法连接在一起的,两种相同类型的序列才能进行连接操作。
2.2.4 乘法
用数字*乘以一个序列会生成新的序列,而在新的序列中,原来的序列将被重复*数字次。
>>> "python" * 5 'pythonpythonpythonpythonpython' >>> [42] * 10 [42, 42, 42, 42, 42, 42, 42, 42, 42, 42]
None、空列表和初始化
空列表可以通过两个中括号进行表示([]),表示什么都没有。如果想创建一个占用十个元素空间,却不包括任何有用内容的列表,可以使用None。None是一个Python的内建值。因此,想初始化一个长度为10的列表,可以按照下面的例子来实现:
>>> sequence = [None] * 10
>>> sequence
[None, None, None, None, None, None, None, None, None, None]
代码清单2-3的程序会再屏幕上打印一个由字符组成的“盒子”,而这个“盒子”在屏幕上居中而且能够根据用户输入的句子自动调整大小。
#以正确的宽度在居中的“盒子”内打印一个句子 sentence = input("Sentence:") screen_width = 80 text_width = len(sentence) box_width = text_width + 6 left_margin = (screen_width - box_width) // 2 print() print(" " * left_margin + "+" + "-" * (box_width - 4) + "+") print(" " * left_margin + "| " + " " * text_width + " |") print(" " * left_margin + "| " + sentence + " |") print(" " * left_margin + "| " + " " * text_width + " |") print(" " * left_margin + "+" + "_" * (box_width - 4) + "+") print()
下面是该例子的运行情况:
Sentence:Hello,World!
+--------------+
| |
| Hello,World! |
| |
+--------------+
2.2.5 成员资格
检查一个元素是否在序列中,可以使用in运算符。这个运算符检查某个条件是否为真,然后返回相应的值:条件为真返回True,条件为假返回False。这样的运算符叫做布尔运算符,而真值则叫做布尔值。
>>> permissions = "rw" >>> "w" in permissions True >>> "x" in permissions False >>> users = ["alex","jack","john"] >>> "slina" in users False >>> input("Enter your user name: ") in users Enter your user name: alex True >>> subject = "$$$ Get rich now!!! $$$" >>> "$$$" in subject True
最初的两个例子使用了成员资格测试分别来检查“w”和“x”是否出现在字符串permissions中。在UNIX系统中,这两行代码可以作为查看文件可写和可执行权限的脚本。接下来的例子则是检查所提供的用户slina是否在用户列表中。最后一个例子可以作为垃圾邮件过滤器的一部分,它可以检查字符串subject是否包含字符串“$$$”。
代码清单2-4给出了一个查看用户输入的用户名和PIN码是否存在于database的列表中的程序。如果存在于列表中,就打印“Access granted”。
#检查用户名和PIN码 database = [ ["albert","1234"], ["smith", "7524"], ["jones", "2234"] ] username = input("User name: ") pin = input("Pin code: ") if [username,pin] in database: print("Access granted")
2.2.6 长度、最小值和最大值
内建函数len,min和max非常有用。len函数返回序列中所包含元素的数量,min函数和max函数则分别返回序列中最大和最小的元素。
>>> number = [110,12,678] >>> len(number) 3 >>> max(number) 678 >>> min(number) 12 >>> max(2,3) 3 >>> min(9,3,2,5) 2
最后两个表达式,参数并不是一个序列,而是以多个数字直接作为参数。
2.3 列表
2.3.1 list函数
因为字符串不能像列表一样被修改,所以有时根据字符串创建列表会很有用。注意,list函数适用于所有类型的序列,而不只是字符串。
>>> list("Hello") ['H', 'e', 'l', 'l', 'o']
2.3.2 基本的列表操作
列表可以使用所有适用于序列的标准操作,例如索引、分片、连接和乘法。可以修改列表的方法:元素赋值、元素删除、分片赋值以及列表方法(append、count、extend、index、insert、pop、remove、reverse、sort和高级排序),请注意并不是所有的列表方法都真正的改变列表。
1.改变列表:元素赋值
给一个变量赋值使用x = 2这种形式,对于列表必须使用索引来标记为某个特定的、位置明确的元素赋值。不能为一个位置不存在的元素进行赋值。
>>> x = [1,1,1] >>> x[1] = 2 >>> x [1, 2, 1]
2.删除元素
使用del语句来实现列表中元素的删除。
>>> del x[2] >>> x [1, 2]
3.分片赋值
>>> name = list("Perl") ['P', 'e', 'r', 'l'] >>> name[2:] = list("ar") >>> name ['P', 'e', 'a', 'r']
程序可以一次为多个元素赋值,虽然每次可以一次一个地赋值,但是当要赋值的序列不等长原序列的时候就没办法了。
>>> name = list("Perl") >>> name[1:] = list("ython") >>> name ['P', 'y', 't', 'h', 'o', 'n']
分片赋值语句可以在不需要替换任何原有元素的情况下插入新元素或序列,同样也可以通过赋值删除元素。
>>> numbers = [1,5] >>> numbers[1:1] = [2,3,4] >>> numbers [1, 2, 3, 4, 5] >>> numbers[1:4] = [] >>> numbers [1, 5] >>> numbers[-1:] = [2,3,4] >>> numbers [1, 2, 3, 4] >>> numbers [::2] = list("ab") >>> numbers ['a', 2, 'b', 4]
2.3.3 列表方法
一般方法可以如下方式调用:
对象.方法(参数)
列表提供了几个方法,用于检查或者修改其中的内容。
1.append(末尾追加)
>>> numbers = [1,2,3,4] >>> numbers.append(5) >>> numbers [1, 2, 3, 4, 5]
2.count
count方法统计某个元素在列表中出现的次数:
>>> ["to","be","or","not","to"].count("to") 2 >>> x = [[1,2],1,1,[2,1,[1,2]]] >>> x.count(1) 2 >>> x.count([1,2]) 1
3.extend
extend方法可以在列表的末尾一次性追加另一个列表中的多个值。
>>> a = [1,2,3] >>> b = [4,5,6] >>> a.extend(b) >>> a [1, 2, 3, 4, 5, 6]
4.index
index方法用户从列表中找出某个值第一个匹配项的索引位置:
>>> knights = ["We","are","the","knights","who"] >>> knights.index("who") 4
5.insert
insert方法用于将对象插入到列表中:
>>> numbers = [1,2,3,5,6,7] >>> numbers.insert(3,"four") >>> numbers [1, 2, 3, 'four', 5, 6, 7]
6.pop
pop方法会移除列表中的一个元素(默认是最后一个),并且返回该元素的值:
>>> x = [1,2,3] >>> x.pop() 3 >>> x [1, 2] >>> x.pop(0) [2] >>> x = [1,2,3] >>> x.append(x.pop()) >>> x [1, 2, 3]
使用pop方法可以实现一种常见的数据结构---栈。栈的原理就像堆放盘子那样。只能在顶部放一个盘子,同样,也只能从顶部移走盘子。最后被放入堆栈的最先被移除(这个原理称为LIFO,即后进先出)。
对于上述两个栈操作(放入和移出),可以称为---入栈(push)和出栈(pop)。
如果需要实现一个先进先出(FIFO)的队列(queue),那么可以使用insert(0,...)来代替append方法。或者也可以继续使用append方法,但必须用pop(0)来代替pop()。
7.remove
remove方法用于移除列表中某个值的第一个匹配项:
>>> x = ["to","be","or","not","to","be"] >>> x.remove("be") >>> x ['to', 'or', 'not', 'to', 'be']
8.reverse
reverse方法将列表中的元素反向存放:
>>> x =[1,2,3] >>> x.reverse() >>> x [3, 2, 1]
9.sort
sort方法用于在原位置对列表进行排序。在“原位置排序”意味着改变原来的列表,从而让其中的元素能按一定的顺序排列,而不是简单地返回一个已排序的列表副本。
>>> x = [4,6,2,1,7,9] >>> x.sort() >>> x [1, 2, 4, 6, 7, 9]
当用户需要一个排好序的列表副本,同时又保留原有列表不变的时候,不能使用如下操作:因为sort方法修改了x却返回了空值,那么最后得到的是已排序的x以及值为None的y。实现这个功能的正确方法是,首先把x的副本赋值给y,然后对y进行排序。
>>> x = [4,6,2,1,7,9] >>> y = x.sort() #Don't do this! >>> print(y) None >>> x = [4,6,2,1,7,9] >>> y = x[:] >>> y.sort() >>> x [4, 6, 2, 1, 7, 9] >>> y [1, 2, 4, 6, 7, 9] >>> y = x >>> y.sort() >>> x [1, 2, 4, 6, 7, 9] >>> y [1, 2, 4, 6, 7, 9]
只是简单地把x赋值给y是没有的,这样做就让x和y都指向同一个列表了。
另一种获取已排序的列表副本的方法是,使用sorted函数:
>>> x = [4,6,2,1,7,9] >>> y = sorted(x) >>> x [4, 6, 2, 1, 7, 9] >>> y [1, 2, 4, 6, 7, 9]
2.4 元组:不可变序列
元组和列表一样,也是一种序列。唯一的不同是元组不能修改。创建元组的语法是:通过圆括号括起来,值用逗号进行分隔。空元组可以用没有包含内容的两个圆括号来表示,实现包含一个值的元组,必须加个逗号,即使只有一个值:
>>> 1,2,3 (1, 2, 3) >>> (1,2,3) (1, 2, 3) >>> () () >>> (42,) (42,) >>> 3*(40+2) 126 >>> 3*(40+2,) (42, 42, 42)
2.4.1 tuple函数
tuple函数的功能与list函数基本上是一样的:以一个序列作为参数并把它转换为元组。如果参数就是元组,那么该参数就会被原样返回:
>>> tuple([1,2,3]) (1, 2, 3) >>> tuple("abc") ('a', 'b', 'c') >>> tuple((1,2,3)) (1, 2, 3)
2.4.2 基本元组操作
除了创建元组和访问元组元素之外,也没有太多其他操作,可以参照其他类型的序列来实现:
>>> x = 1,2,3 >>> x[1] 2 >>> x[0:2] (1, 2)
元组的分片还是元组。
2.5 小结
1.序列
序列是一种数据结构,它包含的元素都进行了编号(从0开始)。典型的序列包括列表、字符串和元组。其中,列表是可变的(可以进行修改),而元组和字符串是不可变的(一旦创建了就是固定的)。通过分片操作可以访问序列的一部分,其中分片需要两个索引号来指出分片的起始和结束位置。要想改变列表,则要对相应的位置进行赋值,或者使用赋值语句重写整个分片。
2.成员资格
in操作符可以检查一个值是否存在于序列中。
3.方法
一些内建类型具有很多有用的方法。
2.5.1 本章的新函数
函数 | 描述 |
cmp(x,y) | 比较两个值 |
len(seq) | 返回序列的长度 |
list(seq) | 把序列转换成列表 |
max(args) | 返回序列或者参数集合中最大值 |
min(args) | 返回序列或者参数集合中最小值 |
reversed(seq) | 对序列进行反向排序 |
sorted(seq) | 返回已排序的包含seq所有元素的列表 |
tuple(seq) | 把序列转换成元组 |