Python学习之2.列表和元组

  数据结构,是指通过某种方式(例如对元素进行编号)组织在一起的数据元素的集合,这些数据元素可以是数字或者字符,甚至可以是其他数据结构。在Python中,最基本的数据结构是序列(sequence)。序列中的每个元素被分配一个序列号,即元素的位置,也称为索引。索引从0开始。

2.1 序列概述

  在Python中包含6中內建的序列,重点讨论最常用的两种类型:列表和元组。其他內建序列有字符串、Unicode字符串、buffer对象和xrange对象。

  列表和元组的主要区别在于,列表可以修改,元组则不能。也就是说如果要根据要求来添加元素,那么列表可能会更好用;而处于某些原因,序列不能修改的时候,使用元组则更为合适。一般来说,在几乎所有的情况下列表都可以替代元组(使用元组作为字典的键,在这种情况下,因为键不可修改,所以就不能使用列表)。在需要操作一组数值的时候,序列很好用。可以用序列表示数据库中一个人的信息,第一个元素表示姓名,第二个元素表示年龄,如下所示:

>>>edward=['Edward Gumby',42]

  同时序列也可以包含其他序列,因此可以构建如下的一个人员信息列表:

>>>edwrad=['Edward Gumby',42]
>>>john=['John Smith',50]
>>>database=[edward,john]
>>>database
[['Edward Gumby',42],['John Smith',50]]

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

2.2 通用序列操作

  所有的序列类型都可以进行某些特定的操作。这些操作包括:索引(indexing)、分片(sliceing)、加(adding)、乘(multiplying)以及检查某个元素是否属于序列的成员(成员资格)。除此之外,Python还有计算序列长度、找出最大元素和最小元素的內建函数。

  序列中的所有元素都是有编号的(从0递增)。这些元素可以通过编号分别访问,如下例所示:

>>>greeting='Hello'
>>>greeting[0]
'H'

  这就是索引。可以通过索引获取元素,所有序列都可以通过这种方法进行索引。使用负数索引时,Python会从右边,也就是从最后1个元素开始计数。最后一个元素的位置编号为-1(不是-0,因为那会和第一个元素重合):

>>>greeting[-1]
'o'

  字符串字面值能够直接使用索引,而不需要一个变量引用它们。效果是一样的,如下所示:

>>>'Hello'[1]
'e'

  如果一个函数返回一个序列,那么可以直接对返回金额过进行索引操作,如下所示:

>>>fourth=raw_input('Year:')[3]
Year: 2005
>>>fourth
'5'

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

>>>tag='<a href="http://www.python.org">Python web site</a>'
>>>tag[9:30]
'http://www.python.org'
>>>tag[32:-4]
'Python web site'

  分片操作对于提取序列的一部分是很有用的。而编号在这里尤为重要。第一个索引是需要提取部分的第一个元素的编号,而最后索引则是分片之后剩下部分的第一个元素的编号:

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

  简而言之,分片操作的实现需要提供两个索引作为边界,第一个索引的元素是包含在分片内的,而第二个则不包含在分片内。
  如果分片所得部分包括序列结尾的元素,那么,只需置空最后一个索引即可:

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

同样,如果分片所得部分包含序列开始的元素,只需置空第一个索引:

>>>numbers[:3]
[1,2,3]

实际上,如果需要复制整个序列,可以将两个序列都置空:

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

  进行分片的时候,分片的起始点和结束点需要进行指定(不管是直接还是间接)。而另外一个参数(步长)通常都是隐式设置的。在普通的分片中,步长是1,分片操作就是按照这个步长逐个遍历序列的元素,然后返回开始和结束点之间的所有元素:

#与numbers[0:10]作用一样,步长默认是1
>>>numbers[0:10:1]   
[1,2,3,4,5,6,7,8,9,10]

  在这个例子中,分片包含了另外一个数字,即步长。如果步长设置成比1大的数,那么就会跳过某些元素:

>>>numbers[0:10:2]
[1,3,5,7,9]

>>>numbers[3:6:3]
[4]

  如果需要将每4个元素的第一个提取出来,那么只要将步长设置为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]
[]

  注意,当时用一个负数作为步长时,必须让开始点大于结束点。否则返回一个空列表。
  通过使用加号可以进行序列的连接操作:

>>>[1,2,3]+[4,5,6]
[1,2,3,4,5,6]
>>>'Hello,'+'world!'
'Hello,world!'

  两种相同类型的序列才能进行连接操作。如列表和字符串是无法连接在一起的,尽管他们都是序列。
  用用数字x乘以一个序列会生成新的序列,而在新的序列中,原来的序列将被重复x次:

>>>'python'*5
'pythonpythonpythonpythonpython'
>>>[42]*10
[42,42,42,42,42,42,42,42,42,42]

  空列表可以简单地通过两个中括号表示[],里面什么都没有。但是如果想创建一个占用十个元素空间,却不包括任何有用内容的列表,可以像前面那样使用[42]10,或者使用[0]10,这会更加实际一些。这就生成了一个包括10个00的列表。可以通过下面的方式来初始化一个长度为10的空列表:

#None是Python的內建值,表示空
>>>sequence=[None]*10
>>>sequence
[None,None,None,None,None,None,None,None,None,None]

  为了检查一个值是否在序列中,可以使用in运算符。该运算符和之前讨论过的有一点不同。这个运算符检查某个条件是否为真,然后返回相应的值:条件为真返回True,条件为假返回False。这样的运算符叫做布尔运算符,而真值则叫做布尔值:

>>>permissions='rw'
>>>'w' in permissions
True
>>>'x' in permissions
False
>>>users=['mlh','foo','bar']
>>>raw_input('Enter your user name: ') in users
Enter your user name: mlh
True
>>>subject='$$$ Get rich now!!! $$$'
>>>'$$$' in subjectt
True

  下面的例子查看用户输入的用户名和PIN码是否存在于数据中:

database=[['albert','1234'],['dilbert','4242'],['smith','7524'],['jones','9843']]
username=raw_input('User name: ')
pin=raw_input('PIN code: ')
if [username,pin] in database:
   print 'Access granted'

  內建函数len、min和max非常有用。len函数返回序列中所包含元素的数量,min函数和max函数则分别返回序列中最大和最小的元素:

>>>numbers=[100,34,678]
>>>len(numbers)
3
>>>max(numbers)
678
>>>min(numbers)
34

>>>max(2,3)
3
>>>min(9,3,2,5)
2

  从上面的例子中,可以看出,max和min函数的参数并不是一个序列,而是以多个数字直接作为参数。
  列表和元组、字符串不同在于列表是可变的,即可以改变列表的内容,并且列表有很多有用的专门的方法。
  因为字符串不能像列表一样被修改,所以有时根据字符串创建列表会很有用。list函数可以实现这个操作:

>>>list('Hello')
['H','e','l','l','o']

  注意,list函数适用于所有类型的序列,而不只是字符串。可以用下面的表达式将一个由字符组成的列表转换为字符串:

#somelist是需要转换的列表
''.join(somelist)

  列表可以使用所有适用于列表的标准操作,例如索引、分片、连接和乘法。有趣的是,列表是可以修改的。改变列表是很容易的,只需要使用普通赋值语句即可。然而,我们并不会使用x=2这样的语句进行赋值,而是使用索引标记来为某个特定的、位置明确的元素赋值,如x[1]=2:

>>>x=[1,1,1]
>>>x[1]=2
>>>x
[1,2,1]

  注意,不能为一个位置不存在的元素进行赋值。
  从列表中删除元素也很容易,使用del语句来实现:

>>>names=['Alice','Beth','Cecil','Dee-Dee','Earl']
>>>del names[2]
>>>names
['Alice','Beth','Dee-Dee','Earl']

  删除后,列表的长度也从5变成了4。除了删除列表中的元素,该语句还能用于删除其他元素。它可以用于字典元素甚至是其他变量的删除操作。
  使用分片赋值时,可以使用与原序列不等长的序列将分片替换:

>>>name=list('Perl')
>>>name
['P','e','r','l']
>>>name[1:]=list('ar')
>>>name
['P','e','a','r']

>>>demo=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,2,3,4,5]
>>>numbers[1:4]=[]
>>>numbers
[1,5]

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

对象.方法(参数)

  除了对象被放置在方法名之前,并且两者之间有一个点号隔开,方法调用和函数调用很类似。

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

    lst=[1,2,3]
    lst.append(4)
    lst
    [1,2,3,4]

  • count:count方法统计某个元素在列表中出现的次数:

    ['to','be','or','not','to','be'].count('to')
    2
    x=[[1,2],1,1,[2,1,[1,2]]]
    x.count(1)
    2
    x.count([1,2])
    1

  • extend:extend方法可以在列表的末尾一次性追加另一个序列中的多个值。换句话说,可以用新列表扩展原有的列表:

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

  这个操作很像连接操作,两者最主要的区别在于:extend方法些改了被扩展的序列(在这个例子中,也就是a),而原始的连接操作则不然,它会返回一个全新的列表:

>>>a=[1,2,3]
>>>b=[4,5,6]
>>>a+b
[1,2,3,4,5,6]
>>>a
[1,2,3]
  • index:index方法用于从列表中找出某个值第一个匹配项的索引位置:

    knights=['we','are','the','knights','who','say','ni']
    knights.index('who')
    4
    knights.index('herring')
    ValueError:list.index(x):x not in list

  • insert:insert方法用于将对象插入到列表中:

    numbers=[1,2,3,4,5,6,7]
    numbers.insert(3,'four')
    numbers
    [1,2,3,'four',5,6,7]

  • pop:pop方法会移除列表中的一个元素(默认是最后一个),并且返回该元素的值:

    x=[1,2,3]
    x.pop()
    3
    x
    [1,2]
    x.pop(0)
    1
    x
    [2]

  pop方法是唯一一个既能修改列表又返回元素值的列表方法。使用pop方法可以实现的一种常用的数据结构--栈。栈的原理就像堆放盘子那样。只能在顶部放一个盘子,同样,也只能从顶部拿走一个盘子。最后被放入堆栈的最先被移除(这个原则称为LIFO,即后进先出)。

  对于上述两个栈操作(放入和移除),即入栈(push)和出栈(pop)。Python没有入栈方法,但可以使用append方法来代替。pop方法和append方法的操作结果恰好相反,如果入栈刚刚出栈的值,最后得到的结果还是原来的栈。

>>>x=[1,2,3]
>>>x.append(x.pop())
>>>x
[1,2,3]
  • remove:remove方法用于移除列表中某个值的第一个匹配项:

    x=['to','be','or','not','to','be']
    x.remove('be')
    x
    ['to','or','not','to','be']

  可以看到,只有第一次出现的值被移除了,而不存在于列表中的值是不会移除的。值得注意的是,remove是一个没有返回值的原位置改变方法。它修改了列表却没有返回值,这与pop方法相反。

  • reverse:reverse方法将列表中的元素反向存放:

    x=[1,2,3]
    x.reverse()
    x
    [3,2,1]

  注意,该方法也改变了列表但不返回值(就像remove和sort)。如果需要对一个序列进行反向迭代,那么可以使用reverse函数,这个函数并不返回一个列表,而是返回一个迭代器(iterator)对象。尽管如此,使用list函数把返回的对象转换成列表也是可行的。

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

    x=[4,5,2,1,7,9]
    x.sort()
    x
    [1,2,4,6,7,9]

  前面介绍过了几个改变列表却不返回值的方法,在大多数情况下这样的行为方式是很合常理的(例如append方法)。当用户需要一个排好序的列表副本,同时又保留原有列表不变的时候,问题就出现了。

  如果希望元素能按特定的方式进行排序,那么可以通过compare(x,y)的形式自定义比较函数。compare(x,y)函数会在x<y时返回负数,在x>y时返回正数,如果x=y则返回0.定义好该函数后,就可以提供给sort方法作为参数了。内建函数cmp提供了比较函数的默认实现方式:

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

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

>>>x=['aardvark','abalone','acme','add','aerate']
>>>x.sort(key=len)
>>>x
['add','acme','aerate','abalone','aardvark']

  另外一个关键字参数reverse是简单的布尔值(True或者是False),用来指明列表是否要进行反向排序。

>>>x=[4,6,2,1,7,9]
>>>x.sort(reverse=True)
>>>x
[9,7,6,4,2,1]

  cmp、key、reverse参数都可以用于sorted函数。在多数情况下,为cmp或key提供自定义函数是非常有用的。
  元组和列表一样,也是序一种序列。唯一不同的是元组不能改变。创建元组的语法如下:

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

  元组是通过圆括号括起来的,空元组可以用没有包含内容的两个圆括号来表示:

>>>()
()

  注意,实现包含一个值的元组有点奇特,必须加一个逗号,即使只有一个值:

>>>42
42
>>>42,
(42,)
>>>(42,)
(42,)
  • tuple:tuple函数的功能与list函数基本上是一样的:以一个序列作为参数并把它转换为元组。如果参数就是元组,那么该参数就会被原样返回:

    tuple([1,2,3])
    x.sort()
    >>>x
    [1,2,4,6,7,9]

      前面介绍过了几个改变列表却不返回值的方法,在大多数情况下这样的行为方式是很合常理的(例如append方法)。当用户需要一个排好序的列表副本,同时又保留原有列表不变的时候,问题就出现了。

      如果希望元素能按特定的方式进行排序,那么可以通过compare(x,y)的形式自定义比较函数。compare(x,y)函数会在x<y时返回负数,在x>y时返回正数,如果x=y则返回0.定义好该函数后,就可以提供给sort方法作为参数了。内建函数cmp提供了比较函数的默认实现方式:

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

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

    >>>x=['aardvark','abalone','acme','add','aerate']
    >>>x.sort(key=len)
    >>>x
    ['add','acme','aerate','abalone','aardvark']

      另外一个关键字参数reverse是简单的布尔值(True或者是False),用来指明列表是否要进行反向排序。

    >>>x=[4,6,2,1,7,9]
    >>>x.sort(reverse=True)
    >>>x
    [9,7,6,4,2,1]

      cmp、key、reverse参数都可以用于sorted函数。在多数情况下,为cmp或key提供自定义函数是非常有用的。
      元组和列表一样,也是序一种序列。唯一不同的是元组不能改变。创建元组的语法如下:

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

      元组是通过圆括号括起来的,空元组可以用没有包含内容的两个圆括号来表示:

    >>>()
    ()

      注意,实现包含一个值的元组有点奇特,必须加一个逗号,即使只有一个值:

    >>>42
    42
    >>>42,
    (42,)
    >>>(42,)
    (42,)

    - tuple:tuple函数的功能与list函数基本上是一样的:以一个序列作为参数并把它转换为元组。如果参数就是元组,那么该参数就会被原样返回:

    >>>tuple([1,2,3])
    (1,2,3)
    tuple('abc')
    ('a','b','c')
    tuple((1,2,3))
    (1,2,3)

  元组其实并不复杂--除了创建元组和访问元组元素之外,也没有太多其他操作,可以参照其他类型的序列来实现:

>>>x=1,2,3
>>>x[1]
2
>>>x[0:2]
(1,2)

  元组的分片还是元组,就像列表的分片还是列表一样

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容

  • 本节要介绍的是Python里面常用的几种数据结构。通常情况下,声明一个变量只保存一个值是远远不够的,我们需要将一组...
    小黑y99阅读 65,190评论 0 9
  • 序 2011年6月8日下午,铃声停止之后,湖北省高考结束,我伴随着谭晶那首循环播放的《龙文》在第五中学满是考生的校...
    GordonTJ阅读 419评论 5 2
  • 人的身体分两部分 头部和躯干 头部用来思考 用来记忆 躯干用来感知 用来体验 其实思考 是一件可怕的事 因为思考太...
    美生活阅读 182评论 4 2
  • 1、 问题:我们下一阶段的事业发展如何? 翻牌数字:18 牌:倒立的权杖三 解读: 整体局势不佳,但是,现在工作上...
    飞姑娘阅读 623评论 0 0
  • 乌鸦 站在 自由女神的肩膀上 它看着我 嗯 一块腐肉 它这样想
    死海没有鱼阅读 173评论 1 1