第2章 列表和元素

数据结构是通过某种方式(例如对元素进行编号) 数据结构,在Python中,最基本的数据结构是序列(sequence).序列中的每个元素分配一个序号->元素的位置,也称为索引.第一个索引是0,第二个则是1.

注意 :
序列中的最后一个元素标记为-1,倒数第二个原始为-2,以此类推.

1. 序列概览

列表和元组的主要区别在于 : 列表可以修改,元组则不能.如 : 使用元组作为字典的键,在这种情况下,因为键不能修改,所以不能用列表.

在需要操作一组数值的时候,序列很好用.可以用序列表示数据库中的一个人信息 : 第1个元素是姓名,第2个元素是年龄.根据上述内容编写一个列表(列表的各个元素通过逗号分隔,写在方括号内),如 :

1
edward = ['kevin',20]

同时,序列也可以包含其他的序列,因此,构建如下的一个人员信息的列表也是可以的,这个列表就是你的数据库 :

1
2
husband = ['kevin',20]
wife =['leona',20]

注意 :
Python之中还有一种名为容器(container)的数据结构,容器基本上是包含其他对象的任意对象.序列(例如列表和元组)和映射(例如字典)是两类主要的容器.序列中的每个元素都有自己的编号,而映射中的每个元素则有一个名字(键).既不是序列也不是映射的容器类型,集合(set)就是一个例子.

2. 通用序列操作

所有序列类型都可以进行某些特定的操作,这些操作包括 : 索引(indexing) / 分片(slicing) / 加(adding) / 乘(multiplying) 以及检查某个元素是否属于序列的成员(成员资格).

迭代(ineration) : 依次对序列中的每个元素重复执行某些操作.

2.1 索引

序列中的所有元素都是有编号的(从0开始递增).这些元素是可以通过编号来访问的.

1
2
3
4
5
6
>>> greeting = 'hello'
>>> greeting[0]
'h'
>>> greeting[-1]
'o'
>>>

注意 :
字符串就是一个由字符组成的序列.索引0指向第1个元素.

我们可以通过索引获取元素,所有序列都可以通过这种方式进行索引.使用负数索引时,Python会从右边,也就是最后一个元素开始计数.最后1个元素的位置编号是-1.

还有一种办法是用'hello[1]',结果为e,效果是一样的.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# Print out a date, given year, month, and day as numbers
months = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
]
# A list with one ending for each number from 1 to 31
endings = ['st', 'nd', 'rd'] + 17 * ['th'] \
+ ['st', 'nd', 'rd'] + 7 * ['th'] \
+ ['st']
year = raw_input('Year: ')
month = raw_input('Month (1-12): ')
day = raw_input('Day (1-31): ')
month_number = int(month)
day_number = int(day)
# Remember to subtract 1 from month and day to get a correct index
month_name = months[month_number-1]
ordinal = day + endings[day_number-1]
print month_name + ' ' + ordinal + ', ' + year

2.2 分片

可以使用分片操作来访问一定范围内的元素,分片通过冒号隔开的两个索引来实现 :

1
2
3
4
5
>>> tag = '<a href="http://www.python.com">Python Web site</a>'
>>> tag[9:30]
'http://www.python.com'
>>> tag[32:-4]
'Python Web site'

简而言之,分片操作的实现需要提供两个索引作为边界,第1个索引的元素是包含在分片内的,而第2个则不包含在分片内.

1
2
3
4
5
6
7
8
>>> numbers = [1,2,3,4,5,6,7,8,9,10]
>>> numbers[3:6]
[4, 5, 6]
>>> numbers[0:1]
[1]
>>> numbers[7:10]
[8, 9, 10]
>>>

优雅的捷径 :

假设需要访问最后的3个元素,那么可以显示的操作:numbers[7:10],现在,索引10指向的是11个元素,但是这个元素是不存在的,却是在最后的一个元素之后.这种做法是可行的.如果需要从列表的结尾开始计数.

1
2
>>> numbers[-3:-1]
[8, 9]

实际上,只要分片中最左边的索引比它右边的晚出现在序列中,结果就是一个空序列.不过,可以使用一个捷径 : 如果分片所得部分包括序列结尾的元素,只需置空最后一个索引即可!

1
2
>>> numbers[-3:]
[8, 9, 10]

可以复制整个序列,可以将两个索引置空

1
2
>>> numbers[:]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

1
2
3
4
5
6
# Split up a URL of the form http://www.something.com
url = raw_input('Please enter the URL: ')
domain = url[11:-4]
print "Domain name: " + domain

更大的步长 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> numbers = [1,2,3,4,5,6,7,8,9,10]
# 分片操作就是按照这个步长逐个便利序列的元素,然后返回开始和结束之间的代码.
>>> numbers[0:10:1]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> numbers[0:10:2]
[1, 3, 5, 7, 9]
# 从索引3到6,取第3个
>>> numbers[3:6:3]
[4]
# 将每4个元素的第1个提取出来
>>> numbers[::4]
[1, 5, 9]
# 步长可以是负数,此时分片从右到左提取元素 :
>>> numbers[8:3:-1]
[9, 8, 7, 6, 5]

当使用一个负数作为步长时,必须让开始点大于结束点.在没有明确指定开始点和结束点的时候,正负数的使用可能会带来些混迹.在python明确规定 : 对于正数步长,python会从序列的头部开始向右提取元素;直到最后一个元素;而对于负数步长,则是从序列的尾部开始向左提取元素,直到最后一个元素.

2.3 序列相加

1
2
3
4
5
6
7
8
9
>>> [1,2,3]+[4,5,6]
[1, 2, 3, 4, 5, 6]
>>> 'hello;+;world!'
'hello;+;world!'
>>> [1,2,3]+'world'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "str") to list
>>>

两种相同的类型的序列才能连接操作.

2.4 乘法

1
2
3
4
5
>>> 'python'*5
'pythonpythonpythonpythonpython'
>>> [42]*10
[42, 42, 42, 42, 42, 42, 42, 42, 42, 42]
>>>

如果想要初始化一个长度为10的空值,可以使用None,None是Python的一个内建值.

1
2
3
4
>>> sum = [None]*10
>>> sum
[None, None, None, None, None, None, None, None, None, None]
>>>

2.5 成员资格

为了检查一个值是否在序列中,可以使用in运算符,这个运算符检查某个条件是否为真,然后返回相应的值.条件为真True,条件为假返回False.

1
2
3
4
5
6
7
8
9
>>> permissions = 'kevin'
>>> 'e'in permissions
True
>>> users = ['mlh','foo','bar']
>>> kevin = raw_input("请输入")
请输入f
>>> print kevin in users
False
>>>
1
2
3
4
5
database = ['kevin','1234']
username = raw_input("请输入用户名")
pin = raw_input("请输入密码")
if(username,pin)in database:
print '登录成功!'

2.6 长度,最小值和最大值.

len函数返回列表中所包含元素的数量,min函数和max函数则分别返回序列中最大和最小的元素.

1
2
3
4
5
6
7
8
9
10
>>> numbers = [100,56,89]
>>> len(numbers)
3
>>> max(numbers)
100
>>> min(numbers)
56
>>> max(7,9)
9
>>>

3. 列表

列表是可变的 : 可以改变列表的内容,并且列表有很多有用的,专门的方法.

3.1 list函数

1
2
>>> list('hello')
['h', 'e', 'l', 'l', 'o']

3.2 基本的列表操作

  • 改变列表 : 元素赋值
    1
    2
    3
    4
    >>> num = [1,2,3,4,5]
    >>> num[2]=22
    >>> num
    [1, 2, 22, 4, 5]

注意
不能为一个位置不存在的元素进行赋值.

  • 删除元素
    从列表中删除元素用del语句来实现 :

    1
    2
    3
    4
    >>> names = ["kevin","shy_kevin","yujiewong"]
    >>> del names[1]
    >>> names
    ['kevin', 'yujiewong']
  • 分片赋值

    1
    2
    3
    4
    5
    6
    >>> name = list('Kevin')
    >>> name
    ['K', 'e', 'v', 'i', 'n']
    >>> name[2:]=list('ar')
    >>> name
    ['K', 'e', 'a', 'r']

3.3 列表方法

方法是一个与某个对象有紧密联系的函数,对象可能是列表,数字,也可能是字符串或者其他的类型的对象.一般来说,方法可以这样进行调用 :
对象.方法(参数)

列表提供了几个方法,用于检查或者修改其中的内容.

  • append
    append方法用于在列表末尾追加新的对象 :

    1
    2
    3
    4
    >>> lst=[1,2,3]
    >>> lst.append(4)
    >>> lst
    [1, 2, 3, 4]
  • count
    count方法统计某个元素在列表中出现的次数 :

    1
    2
    3
    4
    5
    >>> ['what','be','am','is','are'].count('is')
    1
    >>> x=[[1,2],1,1,[2,1,[1,2]]]
    >>> x.count(1)
    2
  • extend
    extend方法可以在列表的末尾一次性追加另一个序列中的多个值.换句话说,可以用新列表扩展原有列表.

    1
    2
    3
    4
    5
    >>> a=[1,2,3,4,5,6]
    >>> b = [1,2,3]
    >>> a.extend(b)
    >>> a
    [1, 2, 3, 4, 5, 6, 1, 2, 3]

它与a+b的区别在于extend方法修改被扩展的序列,而原始的操作则不然,这是因为原始的操作创建了一个包含a和b的副本的新列表,那么连接操作的效率会比extend方法低.

  • index
    index方法用于从列表中找出某个值第一个匹配项的索引位置 :
    1
    2
    3
    >>> names = ['kevin','shy_kevin']
    >>> names.index('kevin')
    0

要注意的是 : 如果搜索到的字符串不存在,会抛出异常.

  • insert
    insert方法用于将对象插入到列表中 :
    1
    2
    >>> numbers=[1,2,3,4,5,6,7]
    >>> numbers,insert(3,'kevin')

与extend方法一样,insert方法的操作也可以用分片赋值来实现.

1
2
3
4
5
>>> numbers
[1, 2, 3, 'kevin', 4, 5, 6, 7]
>>> numbers[3:3] = ['four']
>>> numbers
[1, 2, 3, 'four', 'kevin', 4, 5, 6, 7]

  • pop
    pop方法会移除列表中的一个元素(默认是最后一个),并且返回该元素的值 :
    1
    2
    3
    4
    5
    6
    7
    8
    >>> x = [1,2,3]
    >>> x.pop()
    3
    >>> x.pop(0)
    1
    >>> pop
    >>> x
    [2]

注意
pop方法是唯一一个既能修改列表又返回元素值(除了None)的列表方法.

  • remove
    remove方法用于移除列表中的某个值的第一个匹配项 :

    1
    2
    3
    4
    >>> x = ['to','hello']
    >>> x.remove('hello')
    >>> x
    ['to']
  • reverse
    reverse方法将列表中的元素反向存放

    1
    2
    3
    4
    >>> k = [1,2,3]
    >>> k.reverse()
    >>> k
    [3, 2, 1]
  • sort
    sort方法用于在原位置对列表进行排序.在”原位置排序”意味着改变原来的列表,从而其中的元素能按一定的顺序排列,而不是简单地返回一个已排序的列表副本.

    1
    2
    3
    4
    5
    >>> k
    [3, 2, 1]
    >>> k.sort()
    >>> k
    [1, 2, 3]
  • 高级排序
    可以通过compare(x,y)的形式自定义比较函数,compare(x,y)函数会在xy时返回正数,如果x=y则返回0(根据自定义),定义好该函数后,就可以提供给sort方法作为参数.内建函数cmp提供了比较函数的默认实现方式 :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    >>> cmp(42,32)
    1
    >>> cmp(99,100)
    -1
    >>> cmp(10,10)
    0
    >>> numbers = [1,2,3,4,5,6]
    >>> numbers.sort(cmp)
    >>> numbers
    [1, 2, 3, 4, 5, 6]

sort方法有另外两个可选的参数(key和reverse),如果要使用它们,那么就要通过名字来指定(这个叫关键字参数),参数key和参数cmp类型(必须提供一个在排序过程中使用的函数).然而,该函数并不是用来确定对象的大小,而是为每个元素创建一个键,然后所有元素根据键来排序.因此,如果要根据元素的长度进行排序,那么可以使用len作为键函数.

1
2
3
4
>>> x=['aa','ab','ac']
>>> x.sort(key=len)
>>> x
['aa', 'ab', 'ac']

另一个关键字reverse是简单的布尔值,用来指明列表是否要进行反向排序.

1
2
3
4
5
>>> x=[4,671,2,78,21]
>>> x.sort(reverse=True)
>>> x
[671, 78, 21, 4, 2]
>>>

4. 元组 : 不可变序列

如果你用逗号来分隔开一些值,那么你就会自动创建了元组.

1
2
>>> 1,2,3
(1, 2, 3)

如果想要实现一个值得元组,必须加上逗号.如 : 42,

4.1 tuple函数

以一个序列作为参数并把它转换为元组.

1
2
3
4
5
6
7
>>> tuple([1,2,3,4])
(1, 2, 3, 4)
>>> tuple('abc')
('a', 'b', 'c')
>>> tuple((1,2,3))
(1, 2, 3)
>>>

4.2 元组的意义

  • 元组可以在映射(和集合的成员)中当做键使用,而列表则不行.
  • 元组作为很多内建函数和方法的返回值存在,也就是说你必须对元组进行处理.只要不尝试修改元组.那么,”处理”元组在绝大多数情况下就是把它们当做列表来进行操作.

5. 小结

函数 描述
cmp(x,y) 比较两个值
len(seq) 返回序列的长度
list(seq) 把序列转换成列表
max(args) 返回列表或者参数集合中最大值
min(args) 返回序列或者参数集合中的最小值
reversed(seq) 把序列进行反向迭代
sorted(seq) 返回已排序的包含seq所有元素的列表
tuple(seq) 把序列转换成元组