列表和元组

2021/6/14 18:23:58

本文主要是介绍列表和元组,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

列表和元组

在Python中,最基本的数据结构为序列(sequence)。
序列中的每个元素都有编号,即其位置或索引,其中第一个元素的索引为0,第二个元素的索引
为1,依此类推。
但从0开始指出相对于序列
开头的偏移量。这显得更自然,同时可回绕到序列末尾,用负索引表示序列末尾元素的位置。你
可能认为这种编号方式有点怪,但我敢肯定,你很快就会习惯的。
元组是一种特殊的序列,类
似于列表,只是不能修改。

2.1 序列概述

Python内置了多种序列,本章重点讨论其中最常用的两种:列表和元组。另一种重要的序列
是字符串,将在下一章更详细地讨论。
列表和元组的主要不同在于,列表是可以修改的,而元组不可以。这意味着列表适用于需要
中途添加元素的情形,而元组适用于出于某种考虑需要禁止修改序列的情形。禁止修改序列通常
出于技术方面的考虑,与Python的内部工作原理相关,这也是有些内置函数返回元组的原因所在。
在你自己编写程序时,几乎在所有情况下都可使用列表来代替元组。一种例外情况是将元组用作
字典键,这将在第4章讨论。在这种情况下,不能使用列表来代替元组,因为字典键是不允许修
改的。
在需要处理一系列值时,序列很有用。在数据库中,你可能使用序列来表示人,其中第一个
元素为姓名,而第二个元素为年龄。如果使用列表来表示(所有元素都放在方括号内,并用逗号
隔开),将类似于下面这样:

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

序列还可包含其他序列,因此可创建一个由数据库中所有人员组成的列表:

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

Python支持一种数据结构的基本概念,名为容器(container)。容器基本上就是可包含其
他对象的对象。两种主要的容器是序列(如列表和元组)和映射(如字典)。在序列中,
每个元素都有编号,而在映射中,每个元素都有名称(也叫键)。映射将在第4章详细讨
论。有一种既不是序列也不是映射的容器,它就是集合(set),将在第10章讨论。

2.2 通用的序列操作

有几种操作适用于所有序列,包括索引、切片、相加、相乘和成员资格检查。另外,Python
还提供了一些内置函数,可用于确定序列的长度以及找出序列中最大和最小的元素。

有一个重要的操作这里不会介绍,它就是迭代(iteration)。对序列进行迭代意味着对其
每个元素都执行特定的操作。有关迭代的详细信息,请参阅5.5节。

2.2.1 索引

不同于其他一
些语言,Python没有专门用于表示字符的类型,因此一个字符就是只包含一个元素的字
符串。

这称为索引(indexing)。你可使用索引来获取元素。这种索引方式适用于所有序列。当你使
用负数索引时,Python将从右(即从最后一个元素)开始往左数,因此1是最后一个元素的位置。

对于字符串字面量(以及其他的序列字面量),可直接对其执行索引操作,无需先将其赋给
变量。这与先赋给变量再对变量执行索引操作的效果是一样的。

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

如果函数调用返回一个序列,可直接对其执行索引操作。例如,如果你只想获取用户输入的
年份的第4位,可像下面这样做:

>>> fourth = input('Year: ')[3] 
Year: 2005 
>>> fourth 
'5' 
# 将以数指定年、月、日的日期打印出来
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] 
ordinal = day + endings[day_number-1] 
print(month_name + ' ' + ordinal + ', ' + year) 

这个程序的运行情况类似于下面这样:

Year: 1974 
Month (1-12): 8 
Day (1-31): 16 
August 16th, 1974

2.2.2 切片

除使用索引来访问单个元素外,还可使用切片(slicing)来访问特定范围内的元素。为此,
可使用两个索引,并用冒号分隔:

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

如你所见,切片适用于提取序列的一部分,其中的编号非常重要:第一个索引是包含的第一
个元素的编号,但第二个索引是切片后余下的第一个元素的编号。
简而言之,你提供两个索引来指定切片的边界,其中第一个索引指定的元素包含在切片内,
但第二个索引指定的元素不包含在切片内。

    1. 绝妙的简写
      假设你要访问前述数字列表中的最后三个元素,显然可以明确地指定这一点。
>>> numbers[7:10] 
[8, 9, 10]

在这里,索引10指的是第11个元素:它并不存在,但确实是到达最后一个元素后再前进一步
所处的位置。明白了吗?如果要从列表末尾开始数,可使用负数索引。

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

然而,这样好像无法包含最后一个元素。如果使用索引0,即到达列表末尾后再前进一步所
处的位置,结果将如何呢?

>>> numbers[-3:0] 
[] 

结果并不是你想要的。事实上,执行切片操作时,如果第一个索引指定的元素位于第二个索
引指定的元素后面(在这里,倒数第3个元素位于第1个元素后面),结果就为空序列。好在你能
使用一种简写:如果切片结束于序列末尾,可省略第二个索引。

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

同样,如果切片始于序列开头,可省略第一个索引。

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

实际上,要复制整个序列,可将两个索引都省略。

>>> numbers[:] 
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 
    1. 更大的步长
      执行切片操作时,你显式或隐式地指定起点和终点,但通常省略另一个参数,即步长。在普
      通切片中,步长为1。这意味着从一个元素移到下一个元素,因此切片包含起点和终点之间的所
      有元素。
>>> numbers[0:10:1] 
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

在这个示例中,指定了另一个数。你可能猜到了,这显式地指定了步长。如果指定的步长大
于1,将跳过一些元素。例如,步长为2时,将从起点和终点之间每隔一个元素提取一个元素。

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

显式地指定步长时,也可使用前述简写。例如,要从序列中每隔3个元素提取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]

在这种情况下,要正确地提取颇费思量。如你所见,第一个索引依然包含在内,而第二个索
引不包含在内。步长为负数时,第一个索引必须比第二个索引大。可能有点令人迷惑的是,当你
省略起始和结束索引时,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): 
 File "<pyshell>", line 1, in ? 
 [1, 2, 3] + 'world!' 
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、空列表和初始化
空列表是使用不包含任何内容的两个方括号([])表示的。如果要创建一个可包含10个元素
的列表,但没有任何有用的内容,可像前面那样使用[42]10。但更准确的做法是使用[0]10,
这将创建一个包含10个零的列表。然而,在有些情况下,你可能想使用表示“什么都没有”的值,
如表示还没有在列表中添加任何内容。在这种情况下,可使用None。在Python中,None表示什么
都没有。因此,要将列表的长度初始化为10,可像下面这样做:

>>> sequence = [None] * 10 
>>> sequence 
[None, None, None, None, None, None, None, None, None, None]

代码清单2-3所示的程序在屏幕上打印一个由字符组成的方框。这个方框位于屏幕中央,宽
度取决于用户提供的句子的长度。这些代码看似很复杂,但基本上只使用了算术运算:计算需要
多少个空格、短划线等,以便将内容显示到正确的位置。

2.2.5 成员资格

要检查特定的值是否包含在序列中,可使用运算符in。这个运算符与前面讨论的运算符(如
乘法或加法运算符)稍有不同。它检查是否满足指定的条件,并返回相应的值:满足时返回True,
不满足时返回False。这样的运算符称为布尔运算符,而前述真值称为布尔值。布尔表达式将在
5.4节详细介绍。
下面是一些in运算符的使用示例:

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

开头两个示例使用成员资格测试分别检查'w'和'x'是否包含在字符串变量permissions中。在
UNIX系统中,可在脚本中使用这两行代码来检查对文件的写入和执行权限。接下来的示例检查
提供的用户名mlh是否包含在用户列表中,这在程序需要执行特定的安全策略时很有用(在这种
情况下,可能还需检查密码)。最后一个示例检查字符串变量subject是否包含字符串'$$$',这
可用于垃圾邮件过滤器中。

相比于其他示例,检查字符串是否包含'$$$'的示例稍有不同。一般而言,运算符in检查
指定的对象是否是序列(或其他集合)的成员(即其中的一个元素),但对字符串来说,
只有它包含的字符才是其成员或元素,因此下面的代码完全合理:

>>> 'P' in 'Python' 
True

事实上,在较早的Python版本中,只能对字符串执行这种成员资格检查——确定指定的
字符是否包含在字符串中,但现在可使用运算符in来检查指定的字符串是否为另一个字
符串的子串。



这篇关于列表和元组的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程