在Python中,最基本的数据结构式序列。序列中的每个元素被分配一个序号--即元素的位置,也称为索引。
2.1 序列概览
Python包含6种内建的序列,本章重点讨论最常见的两种类型:列表和元组。其他的内建序列类型有字符串、Unicode字符串、bugger对象和xrange对象。
列表和元组的主要区别在于,列表可以修改,元组则不能。
也就是说如果要根据要求来添加元素,那么列表可能会更好用;
而出于某些原因,序列不能修改的时候,使用元组则更为合适。
使用后者的理由通常是技术性的,它与Python内部的运作方式有关。这也是内建函数可能返回元组的原因。(这句没看懂)
一般来说,在几乎所有情况下列表都可以替代元组(第4章将会介绍一个需要注意的例外情况:使用元组作为字典的键。在这种情况下,因为键不可修改,所以就不能使用列表)。
在需要操作一列数值的时候,序列很好用。可以用序列表示数据库中一个人的信息----第1个元素是姓名,第2个元素是年龄。根据上述内容编写一个列表(列表的各个元素通过逗号分隔,写在方括号中),如下例所示:
>>> edward =['Edward Gumby',42]
同时,序列也可以包含其他的序列,因此,构建如下一个人员信息的列表也是可以的,这个列表就是你的数据库:
>>> edward = ['Edward Gumby',42]
>>> john = ['John Smith',50]
>>>database = [edward,john]
>>>database
[['Edward Gumby',42].['John.Sminth',50]]
注意:Python之中还有一种名为容器的数据结构。容器基本上是包含其他对象的任意对象。
序列(例如列表和元组)和映射(例如字典)是两类主要的容器。序列中的每个元素都有自己的编号,而映射中的每个元素则有一个名字(也称为键)。在第4章会介绍更多有关映射的知识。至于既不是序列也不是映射的容器类型,集合(set)就是一个例子,请参见第10章的相关内容。
2.2 通用序列操作
所有序列类型都可以进行某些特定的操作。这些操作包括:索引、分片、加、乘以及检查某个元素是否属于序列的成员(成员资格)。
除此之外,Python还有计算序列长度、找出最大元素和最小元素的内建函数。
注意:本节有一个重要的操作没有提到----迭代。对序列进行迭代的意思是:依次对序列中的每个元素重复执行某些操作。
2.2.1 索引
序列中的所有元素都是有编号的----从0开始递增。这些元素可以通过编号分别访问,如下例所示:
>>> greeting = 'Hello'
>>> greeting[0]
'H'
注意: 字符串就是一个由字符组成的序列。索引0指向第1个元素,在这个例子中就是字母H。
这就是索引。可以通过索引获取元素。所有序列都可以通过这种方式进行索引。使用负数索引时,Python会从右边,也就是从最后1个元素开始计数。最后1个元素的位置编号是-1(不是-0,因为那会和第1个元素重合);
>>> greeting[-1]
'0'
字符串字面值(就此而言,其他序列字面量亦可)能够直接使用索引,而不需要一个变量引用它们。两种做法的效果是一样的:
>>>'Hello'[1]
'e'
如果一个函数调用返回一个序列,那么可以直接对返回结果进行索引操作。例如,假设你只对用户输入年份的第4个数字感兴趣,那么,可以进行如下操作:
>>> fourth = raw_input('Year: ')[3]
Year:2005
>>>fourth
'5'
以下代码清单是一个示例程序,它要求输入年、月(1~12的数字)、日(1~31),然后打印出相应日期的月份名称,等等。
代码清单 2-1 索引示例
#根据给定的年月日以数字形式打印出日期
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']
---------网上搜到的endings这段的注解:一个月最多有31天,如果按英文表示的话,对应的日期就是
1st, 2nd, 3rd,
4th, ... , 20th, #共有17个,所以是17*['th']
21st, 22nd, 23rd,
24th, ... , 30th, #共有7个,所以是7*['th']
30st
year =raw_input('Year: ')
month= raw_input('Month(1-12): ')
day = raw_input('Day(1-31): ')
month_number =int(month)
day_number = int(day)
#记得要将月份和天数减1,以获得正确的索引
month_name =months[month_number-1]
ordinal =day+endings[day_number-1]
print month_name+' '+ ordinal+'. '+year
以下是程序执行的一部分结果:
Year:2014
Month(1-12):2
Day (1-31):17
February 17th , 2014
2.2.2 分片
与使用索引来访问单个元素类似,可以使用分片操作来访问一定范围内的元素。分片通过冒号相隔的两个索引来实现:
>>> tag ='<a href="http://www.python.org">Python web site</a>'
>>>tag[9:30]
'http://www.python.org'
>>>tag[332:-4]
'Python web site'
分片操作对于提取序列的一部分是很有用的。而编号在这里显得尤为重要。第1个索引是需要提取部分的第1个元素的编号,而最后的索引则是分片之后剩下部分的第1个元素的编号。
请参见如下代码:
>>>numbers = [1,2,3,4,5,6,7,8,9,10]
>>> numbers[3:6]
[4,5,6]
>>> numbers[0:1]
[1]
简而言之,分片操作的实现需要提供两个索引作为边界,第1个索引的元素是包含在分片内的,而第2个则不包含在分片内。
1、优雅的捷径
假设需要访问最后3个元素(根据先前的例子),那么当然可以进行显示的操作:
>>>numbers[7:10]
[8,9,10]
现在,索引10指向的是第11个元素-----这个元素并不存在,却是在最后一个元素之后。明白了吗?
现在,这样的做法是可行的。但是,若需要从列表的结尾开始计数呢?
>>>numbers[-3:-1]
[8,9]
看来并不能以这种方式访问最后的元素。那么使用索引0作为最后一部的下一步操作所使用的元素,结果又会怎样呢?
>>> numbers[-3:0]
[]
这并不是我们所要的结果。实际上,只要分片中最左边的索引比它右边的晚出现在序列中(在这个例子中是倒数第3个比第2个晚出现),结果就是一个空的序列。幸好,可以使用一个捷径:
如果分片所得部分包括序列结尾的元素,那么,只需置空最后一个索引即可:
>>> numbers[-3: ]
[8,9,10]
这种方法同样适用于序列开始的元素:
>>> numbers[ :3]
[1,2,3]
实际上,如果需要复制整个序列,可以将两个索引都置空:
>>> number[ : ]
[1,2,3,4,5,6,7,8,9,10]
代码清单2-2是一个小程序,它会提示输入URL,然后提取域名。
------------chouningning注解:当你读不懂的时候,继续往后读,也许你会找到答案,不要在一个点上纠结。
代码清单2-2 分片示例
# 对http://www.something.com形式的URL进行分割
url = raw_input('Please enter the URL: ')
domain =url[11:-4]
print "Domain name: " +domain
以下是程序运行的实例:
Please enter the URL: http://www.Python.org
Domain name:python
2、更大的步长
进行分片的时候,分片的开始和结束点需要进行指定(不管是直接还是间接)。而另外一个参数(在Python 2.3加入到内建类型)----步长(step length)----通常都是隐式设置的。在普通的分片中,步长是1----分片操作就是按照这个步长逐个遍历序列的元素,然后返回开始和结束点之间的所有元素。
>>> numbers[0:10:1]
[1,2,3,4,5.6,7,8,9,10]
在这个例子中,分片包含了另外一个数字。没错,这就是步长的显式设置。如果步长被设置为比1大的数,那么就会跳过某些元素。例如,步长为2的分片包括的是从开始到结束每隔1个的元素。
>>>numbers[0:10:2]
[1,3,5,7,9]
>>>numbers[3:6:3]
[4]
之前提及的捷径也可以使用。如果需要将每4个元素中的第1个提取出来,那么只要将步长设置为4即可:
>>>numbers[::4]
[1,5,9]
当然,步长不能为0--那不会向下执行--但步长可以是负数,即从右到左提取元素:
>>> numbers[8:3:-1]
[9,8,7,6,5]
>>> numbers[10,0:-2]
[10,8,6,4,2]
>>>numbers[0:10:-2]
[]
>>>numbers[::-2]
[10,8,6,4,2]
>>> numbers[5::-2]
[6,4,2]
>>> numbers[:5:-2]
[10,8]
chouningning注解:这里的5代表序列里的6,所以6不在分片内。从数字10,9,8,7 四个数字中从右到左步长为2提取元素。所以是10,8。
在这里要得到正确的分片结果需要动些脑筋。开始点的元素(最左边元素)包括在结果之中,而结束点的元素(最右边的元素)则不在分片之内。当使用一个负数作为步长时,必须让开始点(开始索引)大于结束点。在没有明确指定开始点和结束点的时候,正负数的使用可能会带来一些混淆。不过再这种情况下Python会进行正确的操作:对于一个正数步长,Python会从序列的头部开始向右提取元素,直到最后一个元素;而对于负数步长,则是从序列的尾部开始向左提取元素,直到第一个元素。
2.2.3 序列相加
通过使用加号可以进行序列的连接操作:
>>> [1,2,3]+[4,5,6]
[1,2,3,4,5,6]
>>>'Hello, '+ 'world!'
'Hello, world!'
>>> [1,2,3]+'world!'
Traceback (innermost last):
TypeError: can only concatenate list (not "string") to list
正如错误信息所提示的,列表和字符串时无法连接在一起的,尽管它们都是序列。简单来说,两种相同类型的序列才能进行连接操作。
2.2.4 乘法
用数字x乘以一个序列会生成新的序列,而在新的序列中,原来的序列将被重复x次。
>>> 'python' * 5
'pythonpythonpythonpythonpython'
>>>[42]*10
[42,42,42,42,42,42,42,42,42,42]
None、空列表和初始化
空列表可以简单地通过两个中括号进行标示([]) ------里面什么东西都没有。但是,如果想创建一个占用十个元素的空间,却不包括任何有用内容的列表,又该怎么办呢?可以像前面那样使用[42]*10,或者使用[0]*10,这会更加实际一些。这样就生成了一个包括10个0的列表。然而,有时候可能会需要一个值来代表空值----意味着没有在里面放置任何元素。这个时候就需要使用None。None是一个Python的内建值,它的确切含义是“这里什么也没有”。因此,如果想初始化一个长度为10的列表,可以按照下面的例子来实现:
>>>sequence = [None]*10
>>>sequence
[None,None,None,None,None,None,None,None,None,None]
代码清单2-3的程序会在屏幕上打印一个由字符组成的“盒子”,而这个“盒子”在屏幕上居中而且能根据用户输入的句子自动调整大小。
代码可能看起来很复杂,但只使用基本的算法----计算出有多少空格、破折号等字符,然后将它们放置到合适的位置即可。
代码清单2-3 序列(字符串)乘法示例
# 以正确的宽度在居中的盒子”内打印一个句子
# 注意,整数除法运算符(//)只能用在Python 2.2以及后续版本,在之前的版本中,只使用普通除法(/)
sentence =raw_input("Sentence: ")
screen_width =80
text_width =len (Sentence)
box_width =text_width + 6
left_margin = (screen_width -box_width) //2
print ' '* left_margin +'+' +'-'*(box_width-2) + '+'
print ' '* left_margin +'|+''*text_width + '|'
------------Page 50
chouningning注解:按作者的代码,上下的两条线要长于中间部分。总觉得第3和倒数第3行应该是' '*box_width
试了下作者的,得不到书中附图的效果。
但是,用以下自己修改过的代码可以达到图中效果:
print
print' '*left_margin+'+'+'-'*(box_width-2)+'+'
print' '*left_margin+'|'+' '*(box_width-2)+ '|'
print' '*left_margin+'|'+' '*((box_width-text_width-2)//2)+sentence+' '*((box_width-text_width-2)//2)+'|'
print' '*left_margin+'|'+' '*(box_width-2)+ '|'
print' '*left_margin+'+'+'-'*(box_width-2)+'+'
print
附图:
移步网易博客:优快云的文本编辑器真的很渣。