1.5

>>> 3 / 2.0

1.5

>>> 3.0 / 2.0

1.5

从Python 3转而用Python 2或从Python 2转而用Python 3时,这种除法行为常常会令人迷惑。使用或编写同时使用浮点数和整数的代码时,一定要注意这种异常行为。

动手试一试

2-8 数字8 : 编写4个表达式,它们分别使用加法、减法、乘法和除法运算,但结果都是数字8。为使用print 语句来显示结果,务必将这些表达式用括号括起来,也

就是说,你应该编写4行类似于下面的代码:

print(5 + 3)

输出应为4行,其中每行都只包含数字8。

2-9 最喜欢的数字: 将你最喜欢的数字存储在一个变量中,再使用这个变量创建一条消息,指出你最喜欢的数字,然后将这条消息打印出来。

2.5  注释

在大多数编程语言中,注释都是一项很有用的功能。本书前面编写的程序中都只包含Python代码,但随着程序越来越大、越来越复杂,就应在其中添加说明,对你解决问题的方法

进行大致的阐述。注释 让你能够使用自然语言在程序中添加说明。

2.5.1  如何编写注释

在Python中,注释用井号(# )标识。井号后面的内容都会被Python解释器忽略,如下所示:

comment.py

向大家问好

print("Hello Python people!")

Python解释器将忽略第1行,只执行第2行。

Hello Python people!

2.5.2  该编写什么样的注释

编写注释的主要目的是阐述代码要做什么,以及是如何做的。在开发项目期间,你对各个部分如何协同工作了如指掌,但过段时间后,有些细节你可能不记得了。当然,你总是

可以通过研究代码来确定各个部分的工作原理,但通过编写注释,以清晰的自然语言对解决方案进行概述,可节省很多时间。

要成为专业程序员或与其他程序员合作,就必须编写有意义的注释。当前,大多数软件都是合作编写的,编写者可能是同一家公司的多名员工,也可能是众多致力于同一个开源

项目的人员。训练有素的程序员都希望代码中包含注释,因此你最好从现在开始就在程序中添加描述性注释。作为新手,最值得养成的习惯之一是,在代码中编写清晰、简洁的

注释。

如果不确定是否要编写注释,就问问自己,找到合理的解决方案前,是否考虑了多个解决方案。如果答案是肯定的,就编写注释对你的解决方案进行说明吧。相比回过头去再添

加注释,删除多余的注释要容易得多。从现在开始,本书的示例都将使用注释来阐述代码的工作原理。

动手试一试

2-10 添加注释: 选择你编写的两个程序,在每个程序中都至少添加一条注释。如果程序太简单,实在没有什么需要说明的,就在程序文件开头加上你的姓名和当前日

期,再用一句话阐述程序的功能。

2.6  Python 之禅

编程语言Perl曾在互联网领域长期占据着统治地位,早期的大多数交互式网站使用的都是Perl脚本。彼时,“解决问题的办法有多个”被Perl社区奉为座右铭。这种理念一度深受大家

的喜爱,因为这种语言固有的灵活性使得大多数问题都有很多不同的解决之道。在开发项目期间,这种灵活性是可以接受的,但大家最终认识到,过于强调灵活性会导致大型项

目难以维护:要通过研究代码搞清楚当时解决复杂问题的人是怎么想的,既困难又麻烦,还会耗费大量的时间。

经验丰富的程序员倡导尽可能避繁就简。Python社区的理念都包含在Tim Peters撰写的“Python之禅”中。要获悉这些有关编写优秀Python代码的指导原则,只需在解释器中执行命

令import this 。这里不打算赘述整个“Python之禅”,而只与大家分享其中的几条原则,让你明白为何它们对Python新手来说至关重要。

>>> import this

The Zen of Python, by Tim Peters

Beautiful is better than ugly.

Python程序员笃信代码可以编写得漂亮而优雅。编程是要解决问题的,设计良好、高效而漂亮的解决方案都会让程序员心生敬意。随着你对Python的认识越来越深入,并使用它来

编写越来越多的代码,有一天也许会有人站在你后面惊呼:“哇,代码编写得真是漂亮!”

Simple is better than complex.

如果有两个解决方案,一个简单,一个复杂,但都行之有效,就选择简单的解决方案吧。这样,你编写的代码将更容易维护,你或他人以后改进这些代码时也会更容易。

Complex is better than complicated.

现实是复杂的,有时候可能没有简单的解决方案。在这种情况下,就选择最简单可行的解决方案吧。

Readability counts.

即便是复杂的代码,也要让它易于理解。开发的项目涉及复杂代码时,一定要为这些代码编写有益的注释。

There should be one— and preferably only one —obvious way to do it.

如果让两名Python程序员去解决同一个问题,他们提供的解决方案应大致相同。这并不是说编程没有创意空间,而是恰恰相反!然而,大部分编程工作都是使用常见解决方案来解

决简单的小问题,但这些小问题都包含在更庞大、更有创意空间的项目中。在你的程序中,各种具体细节对其他Python程序员来说都应易于理解。

Now is better than never.

你可以将余生都用来学习Python和编程的纷繁难懂之处,但这样你什么项目都完不成。不要企图编写完美无缺的代码;先编写行之有效的代码,再决定是对其做进一步改进,还是

转而去编写新代码。

等你进入下一章,开始研究更复杂的主题时,务必牢记这种简约而清晰的理念。如此,经验丰富的程序员定将对你编写的代码心生敬意,进而乐意向你提供反馈,并与你合作开

发有趣的项目。

动手试一试

2-11 Python 之禅: 在Python终端会话中执行命令import this ,并粗略地浏览一下其他的指导原则。

2.7  小结

在本章中,你学习了:如何使用变量;如何创建描述性变量名以及如何消除名称错误和语法错误;字符串是什么,以及如何使用小写、大写和首字母大写方式显示字符串;使用

空白来显示整洁的输出,以及如何剔除字符串中多余的空白;如何使用整数和浮点数;使用数值数据时需要注意的意外行为。你还学习了如何编写说明性注释,让代码对你和其

他人来说更容易理解。最后,你了解了让代码尽可能简单的理念。

在第3章,你将学习如何在被称为列表 的变量中存储信息集,以及如何通过遍历列表来操作其中的信息。

第 3 章 列表简介

01 - 图1

在本章和下一章中,你将学习列表是什么以及如何使用列表元素。列表让你能够在一个地方存储成组的信息,其中可以只包含几个元素,也可以包含数百万个元素。

列表是新手可直接使用的最强大的Python功能之一,它融合了众多重要的编程概念。

3.1  列表是什么

列表 由一系列按特定顺序排列的元素组成。你可以创建包含字母表中所有字母、数字0~9或所有家庭成员姓名的列表;也可以将任何东西加入列表中,其中的元素之间可以没有

任何关系。鉴于列表通常包含多个元素,给列表指定一个表示复数的名称(如letters 、digits 或names )是个不错的主意。

在Python中,用方括号([] )来表示列表,并用逗号来分隔其中的元素。下面是一个简单的列表示例,这个列表包含几种自行车:

bicycles.py

bicycles = ['trek', 'cannondale', 'redline', 'specialized']

print(bicycles)

如果你让Python将列表打印出来,Python将打印列表的内部表示,包括方括号:

['trek', 'cannondale', 'redline', 'specialized']

鉴于这不是你要让用户看到的输出,下面来学习如何访问列表元素。

3.1.1  访问列表元素

列表是有序集合,因此要访问列表的任何元素,只需将该元素的位置或索引告诉Python即可。要访问列表元素,可指出列表的名称,再指出元素的索引,并将其放在方括号内。

例如,下面的代码从列表bicycles 中提取第一款自行车:

bicycles = ['trek', 'cannondale', 'redline', 'specialized']

❶ print(bicycles[0])

❶处演示了访问列表元素的语法。当你请求获取列表元素时,Python只返回该元素,而不包括方括号和引号:

trek

这正是你要让用户看到的结果——整洁、干净的输出。

你还可以对任何列表元素调用第2章介绍的字符串方法。例如,可使用方法title() 让元素'trek' 的格式更整洁:

bicycles = ['trek', 'cannondale', 'redline', 'specialized']

print(bicycles[0].title())

这个示例的输出与前一个示例相同,只是首字母T是大写的。

3.1.2  索引从0 而不是1 开始

在Python中,第一个列表元素的索引为0,而不是1。在大多数编程语言中都是如此,这与列表操作的底层实现相关。如果结果出乎意料,请看看你是否犯了简单的差一错误。

第二个列表元素的索引为1。根据这种简单的计数方式,要访问列表的任何元素,都可将其位置减1,并将结果作为索引。例如,要访问第四个列表元素,可使用索引3。

下面的代码访问索引1 和3 处的自行车:

bicycles = ['trek', 'cannondale', 'redline', 'specialized']

print(bicycles[1])

print(bicycles[3])

这些代码返回列表中的第二个和第四个元素:

cannondale

specialized

Python为访问最后一个列表元素提供了一种特殊语法。通过将索引指定为-1 ,可让Python返回最后一个列表元素:

bicycles = ['trek', 'cannondale', 'redline', 'specialized']

print(bicycles[-1])

这些代码返回'specialized' 。这种语法很有用,因为你经常需要在不知道列表长度的情况下访问最后的元素。这种约定也适用于其他负数索引,例如,索引-2 返回倒数第

二个列表元素,索引-3 返回倒数第三个列表元素,以此类推。

3.1.3  使用列表中的各个值

可像使用其他变量一样使用列表中的各个值。例如,你可以使用拼接根据列表中的值来创建消息。

下面来尝试从列表中提取第一款自行车,并使用这个值来创建一条消息:

bicycles = ['trek', 'cannondale', 'redline', 'specialized']

❶ message = "My first bicycle was a " + bicycles[0].title() + "."

print(message)

我们使用bicycles[0] 的值生成了一个句子,并将其存储在变量message 中(见❶)。输出是一个简单的句子,其中包含列表中的第一款自行车:

My first bicycle was a Trek.

动手试一试

请尝试编写一些简短的程序来完成下面的练习,以获得一些使用Python列表的第一手经验。你可能需要为每章的练习创建一个文件夹,以整洁有序的方式存储为完成各

章的练习而编写的程序。

3-1 姓名: 将一些朋友的姓名存储在一个列表中,并将其命名为names 。依次访问该列表中的每个元素,从而将每个朋友的姓名都打印出来。

3-2 问候语: 继续使用练习3-1中的列表,但不打印每个朋友的姓名,而为每人打印一条消息。每条消息都包含相同的问候语,但抬头为相应朋友的姓名。

3-3 自己的列表: 想想你喜欢的通勤方式,如骑摩托车或开汽车,并创建一个包含多种通勤方式的列表。根据该列表打印一系列有关这些通勤方式的宣言,如“I would like to own a Honda motorcycle”。

3.2  修改、添加和删除元素

你创建的大多数列表都将是动态的,这意味着列表创建后,将随着程序的运行增删元素。例如,你创建一个游戏,要求玩家射杀从天而降的外星人;为此,可在开始时将一些外

星人存储在列表中,然后每当有外星人被射杀时,都将其从列表中删除,而每次有新的外星人出现在屏幕上时,都将其添加到列表中。在整个游戏运行期间,外星人列表的长度

将不断变化。

3.2.1  修改列表元素

修改列表元素的语法与访问列表元素的语法类似。要修改列表元素,可指定列表名和要修改的元素的索引,再指定该元素的新值。

例如,假设有一个摩托车列表,其中的第一个元素为'honda' ,如何修改它的值呢?

motorcycles.py

❶ motorcycles = ['honda', 'yamaha', 'suzuki']

print(motorcycles)

❷ motorcycles[0] = 'ducati'

print(motorcycles)

我们首先定义一个摩托车列表,其中的第一个元素为'honda' (见❶)。接下来,我们将第一个元素的值改为'ducati' (见❷)。输出表明,第一个元素的值确实变了,但

其他列表元素的值没变:

['honda', 'yamaha', 'suzuki']

['ducati', 'yamaha', 'suzuki']

你可以修改任何列表元素的值,而不仅仅是第一个元素的值。

3.2.2  在列表中添加元素

你可能出于众多原因要在列表中添加新元素,例如,你可能希望游戏中出现新的外星人、添加可视化数据或给网站添加新注册的用户。Python提供了多种在既有列表中添加新数据

的方式。

  1. 在列表末尾添加元素

在列表中添加新元素时,最简单的方式是将元素附加到列表末尾。给列表附加元素时,它将添加到列表末尾。继续使用前一个示例中的列表,在其末尾添加新元素'ducati' :

motorcycles = ['honda', 'yamaha', 'suzuki']

print(motorcycles)

❶ motorcycles.append('ducati')

print(motorcycles)

方法append() 将元素'ducati' 添加到了列表末尾(见❶ ),而不影响列表中的其他所有元素:

['honda', 'yamaha', 'suzuki']

['honda', 'yamaha', 'suzuki', 'ducati']

方法append() 让动态地创建列表易如反掌,例如,你可以先创建一个空列表,再使用一系列的append() 语句添加元素。下面来创建一个空列表,再在其中添加元

素'honda' 、'yamaha' 和'suzuki' :

motorcycles = []

motorcycles.append('honda')

motorcycles.append('yamaha')

motorcycles.append('suzuki')

print(motorcycles)

最终的列表与前述示例中的列表完全相同:

['honda', 'yamaha', 'suzuki']

这种创建列表的方式极其常见,因为经常要等程序运行后,你才知道用户要在程序中存储哪些数据。为控制用户,可首先创建一个空列表,用于存储用户将要输入的值,然后将

用户提供的每个新值附加到列表中。

  1. 在列表中插入元素

使用方法insert() 可在列表的任何位置添加新元素。为此,你需要指定新元素的索引和值。

motorcycles = ['honda', 'yamaha', 'suzuki']

❶ motorcycles.insert(0, 'ducati')

print(motorcycles)

在这个示例中,值'ducati' 被插入到了列表开头(见❶);方法insert() 在索引0 处添加空间,并将值'ducati' 存储到这个地方。这种操作将列表中既有的每个元素都右

移一个位置:

['ducati', 'honda', 'yamaha', 'suzuki']

3.2.3  从列表中删除元素

你经常需要从列表中删除一个或多个元素。例如,玩家将空中的一个外星人射杀后,你很可能要将其从存活的外星人列表中删除;当用户在你创建的Web应用中注销其账户时,

你需要将该用户从活跃用户列表中删除。你可以根据位置或值来删除列表中的元素。

  1. 使用del 语句删除元素

如果知道要删除的元素在列表中的位置,可使用del 语句。

motorcycles = ['honda', 'yamaha', 'suzuki']

print(motorcycles)

❶ del motorcycles[0]

print(motorcycles)

❶ 处的代码使用del 删除了列表motorcycles 中的第一个元素——'honda' :

['honda', 'yamaha', 'suzuki']

['yamaha', 'suzuki']

使用del 可删除任何位置处的列表元素,条件是知道其索引。下例演示了如何删除前述列表中的第二个元素——'yamaha' :

motorcycles = ['honda', 'yamaha', 'suzuki']

print(motorcycles)

del motorcycles[1]

print(motorcycles)

下面的输出表明,已经将第二款摩托车从列表中删除了:

['honda', 'yamaha', 'suzuki']

['honda', 'suzuki']

在这两个示例中,使用del 语句将值从列表中删除后,你就无法再访问它了。

  1. 使用方法pop() 删除元素

有时候,你要将元素从列表中删除,并接着使用它的值。例如,你可能需要获取刚被射杀的外星人的 x 和 y 坐标,以便在相应的位置显示爆炸效果;在Web应用程序中,你可能

要将用户从活跃成员列表中删除,并将其加入到非活跃成员列表中。

方法pop() 可删除列表末尾的元素,并让你能够接着使用它。术语弹出 (pop)源自这样的类比:列表就像一个栈,而删除列表末尾的元素相当于弹出栈顶元素。

下面从列表motorcycles 中弹出一款摩托车:

❶ motorcycles = ['honda', 'yamaha', 'suzuki']

print(motorcycles)

❷ popped_motorcycle = motorcycles.pop()

❸ print(motorcycles)

❹ print(popped_motorcycle)

我们首先定义并打印了列表motorcycles (见❶)。接下来,我们从这个列表中弹出一个值,并将其存储到变量popped_motorcycle 中(见❷)。然后我们打印这个列

表,以核实从其中删除了一个值(见❸)。最后,我们打印弹出的值,以证明我们依然能够访问被删除的值(见❹)。

输出表明,列表末尾的值'suzuki' 已删除,它现在存储在变量popped_motorcycle 中:

['honda', 'yamaha', 'suzuki']

['honda', 'yamaha']

suzuki

方法pop() 是怎么起作用的呢?假设列表中的摩托车是按购买时间存储的,就可使用方法pop() 打印一条消息,指出最后购买的是哪款摩托车:

motorcycles = ['honda', 'yamaha', 'suzuki']

last_owned = motorcycles.pop()

print("The last motorcycle I owned was a " + last_owned.title() + ".") 输出是一个简单的句子,指出了最新购买的是哪款摩托车:

The last motorcycle I owned was a Suzuki.

  1. 弹出列表中任何位置处的元素

实际上,你可以使用pop() 来删除列表中任何位置的元素,只需在括号中指定要删除的元素的索引即可。

motorcycles = ['honda', 'yamaha', 'suzuki']

❶ first_owned = motorcycles.pop(0)

❷ print('The first motorcycle I owned was a ' + first_owned.title() + '.') 首先,我们弹出了列表中的第一款摩托车(见❶),然后打印了一条有关这辆摩托车的消息(见❷)。输出是一个简单的句子,描述了我购买的第一辆摩托车:

The first motorcycle I owned was a Honda.

别忘了,每当你使用pop() 时,被弹出的元素就不再在列表中了。

如果你不确定该使用del 语句还是pop() 方法,下面是一个简单的判断标准:如果你要从列表中删除一个元素,且不再以任何方式使用它,就使用del 语句;如果你要在删除元

素后还能继续使用它,就使用方法pop() 。

  1. 根据值删除元素

有时候,你不知道要从列表中删除的值所处的位置。如果你只知道要删除的元素的值,可使用方法remove() 。

例如,假设我们要从列表motorcycles 中删除值'ducati' 。

motorcycles = ['honda', 'yamaha', 'suzuki', 'ducati']

print(motorcycles)

❶ motorcycles.remove('ducati')

print(motorcycles)

❶ 处的代码让Python确定'ducati' 出现在列表的什么地方,并将该元素删除:

['honda', 'yamaha', 'suzuki', 'ducati']

['honda', 'yamaha', 'suzuki']

使用remove() 从列表中删除元素时,也可接着使用它的值。下面删除值'ducati' ,并打印一条消息,指出要将其从列表中删除的原因:

❶ motorcycles = ['honda', 'yamaha', 'suzuki', 'ducati']

print(motorcycles)

❷ too_expensive = 'ducati'

❸ motorcycles.remove(too_expensive)

print(motorcycles)

❹ print("\nA " + too_expensive.title() + " is too expensive for me.") 在❶处定义列表后,我们将值'ducati' 存储在变量too_expensive 中(见❷)。接下来,我们使用这个变量来告诉Python将哪个值从列表中删除(见❸)。最后,

值'ducati' 已经从列表中删除,但它还存储在变量too_expensive 中(见❹),让我们能够打印一条消息,指出将'ducati' 从列表motorcycles 中删除的原因:

['honda', 'yamaha', 'suzuki', 'ducati']

['honda', 'yamaha', 'suzuki']

A Ducati is too expensive for me.

>

注意  方法remove() 只删除第一个指定的值。如果要删除的值可能在列表中出现多次,就需要使用循环来判断是否删除了所有这样的值。你将在第7章学习如何这

样做。

动手试一试

下面的练习比第2章的练习要复杂些,但让你有机会以前面介绍过的各种方式使用列表。

3-4 嘉宾名单 :如果你可以邀请任何人一起共进晚餐(无论是在世的还是故去的),你会邀请哪些人?请创建一个列表,其中包含至少3个你想邀请的人;然后,使用

这个列表打印消息,邀请这些人来与你共进晚餐。

3-5 修改嘉宾名单 :你刚得知有位嘉宾无法赴约,因此需要另外邀请一位嘉宾。

以完成练习3-4时编写的程序为基础,在程序末尾添加一条print 语句,指出哪位嘉宾无法赴约。

修改嘉宾名单,将无法赴约的嘉宾的姓名替换为新邀请的嘉宾的姓名。

再次打印一系列消息,向名单中的每位嘉宾发出邀请。

3-6 添加嘉宾 :你刚找到了一个更大的餐桌,可容纳更多的嘉宾。请想想你还想邀请哪三位嘉宾。

以完成练习3-4或练习3-5时编写的程序为基础,在程序末尾添加一条print 语句,指出你找到了一个更大的餐桌。

使用insert() 将一位新嘉宾添加到名单开头。

使用insert() 将另一位新嘉宾添加到名单中间。

使用append() 将最后一位新嘉宾添加到名单末尾。

打印一系列消息,向名单中的每位嘉宾发出邀请。

3-7 缩减名单 :你刚得知新购买的餐桌无法及时送达,因此只能邀请两位嘉宾。

以完成练习3-6时编写的程序为基础,在程序末尾添加一行代码,打印一条你只能邀请两位嘉宾共进晚餐的消息。

使用pop() 不断地删除名单中的嘉宾,直到只有两位嘉宾为止。每次从名单中弹出一位嘉宾时,都打印一条消息,让该嘉宾知悉你很抱歉,无法邀请他来共进

晚餐。

对于余下的两位嘉宾中的每一位,都打印一条消息,指出他依然在受邀人之列。

使用del 将最后两位嘉宾从名单中删除,让名单变成空的。打印该名单,核实程序结束时名单确实是空的。

3.3  组织列表

在你创建的列表中,元素的排列顺序常常是无法预测的,因为你并非总能控制用户提供数据的顺序。这虽然在大多数情况下都是不可避免的,但你经常需要以特定的顺序呈现信

息。有时候,你希望保留列表元素最初的排列顺序,而有时候又需要调整排列顺序。Python提供了很多组织列表的方式,可根据具体情况选用。

3.3.1  使用方法sort() 对列表进行永久性排序

Python方法sort() 让你能够较为轻松地对列表进行排序。假设你有一个汽车列表,并要让其中的汽车按字母顺序排列。为简化这项任务,我们假设该列表中的所有值都是小写

的。

cars.py

cars = ['bmw', 'audi', 'toyota', 'subaru']

❶ cars.sort()

print(cars)

方法sort() (见❶)永久性地修改了列表元素的排列顺序。现在,汽车是按字母顺序排列的,再也无法恢复到原来的排列顺序:

['audi', 'bmw', 'subaru', 'toyota']

你还可以按与字母顺序相反的顺序排列列表元素,为此,只需向sort() 方法传递参数reverse=True 。下面的示例将汽车列表按与字母顺序相反的顺序排列:

cars = ['bmw', 'audi', 'toyota', 'subaru']

cars.sort(reverse=True)

print(cars)

同样,对列表元素排列顺序的修改是永久性的:

['toyota', 'subaru', 'bmw', 'audi']

3.3.2  使用函数sorted() 对列表进行临时排序

要保留列表元素原来的排列顺序,同时以特定的顺序呈现它们,可使用函数sorted() 。函数sorted() 让你能够按特定顺序显示列表元素,同时不影响它们在列表中的原始排

列顺序。

下面尝试对汽车列表调用这个函数。

cars = ['bmw', 'audi', 'toyota', 'subaru']

❶ print("Here is the original list:")

print(cars)

❷ print("\nHere is the sorted list:")

print(sorted(cars))

❸ print("\nHere is the original list again:")

print(cars)

我们首先按原始顺序打印列表(见❶),再按字母顺序显示该列表(见❷)。以特定顺序显示列表后,我们进行核实,确认列表元素的排列顺序与以前相同(见❸)。

Here is the original list:

['bmw', 'audi', 'toyota', 'subaru']

Here is the sorted list:

['audi', 'bmw', 'subaru', 'toyota']

❹ Here is the original list again:

['bmw', 'audi', 'toyota', 'subaru']

注意,调用函数sorted() 后,列表元素的排列顺序并没有变(见❹)。如果你要按与字母顺序相反的顺序显示列表,也可向函数sorted() 传递参数reverse=True 。

注意  在并非所有的值都是小写时,按字母顺序排列列表要复杂些。决定排列顺序时,有多种解读大写字母的方式,要指定准确的排列顺序,可能比我们这里所做的

要复杂。然而,大多数排序方式都基于本节介绍的知识。

3.3.3  倒着打印列表

要反转列表元素的排列顺序,可使用方法reverse() 。假设汽车列表是按购买时间排列的,可轻松地按相反的顺序排列其中的汽车:

cars = ['bmw', 'audi', 'toyota', 'subaru']

print(cars)

cars.reverse()

print(cars)

注意,reverse() 不是指按与字母顺序相反的顺序排列列表元素,而只是反转列表元素的排列顺序:

['bmw', 'audi', 'toyota', 'subaru']

['subaru', 'toyota', 'audi', 'bmw']

方法reverse() 永久性地修改列表元素的排列顺序,但可随时恢复到原来的排列顺序,为此只需对列表再次调用reverse() 即可。

3.3.4  确定列表的长度

使用函数len() 可快速获悉列表的长度。在下面的示例中,列表包含4个元素,因此其长度为4:

>>> cars = ['bmw', 'audi', 'toyota', 'subaru']

>>> len(cars)

4

在你需要完成如下任务时,len() 很有用:确定还有多少个外星人未被射杀,需要管理多少项可视化数据,网站有多少注册用户等。

注意  Python计算列表元素数时从1开始,因此确定列表长度时,你应该不会遇到差一错误。

动手试一试

3-8 放眼世界 :想出至少5个你渴望去旅游的地方。

将这些地方存储在一个列表中,并确保其中的元素不是按字母顺序排列的。

按原始排列顺序打印该列表。不要考虑输出是否整洁的问题,只管打印原始Python列表。

使用sorted() 按字母顺序打印这个列表,同时不要修改它。

再次打印该列表,核实排列顺序未变。

使用sorted() 按与字母顺序相反的顺序打印这个列表,同时不要修改它。

再次打印该列表,核实排列顺序未变。

使用reverse() 修改列表元素的排列顺序。打印该列表,核实排列顺序确实变了。

使用reverse() 再次修改列表元素的排列顺序。打印该列表,核实已恢复到原来的排列顺序。

使用sort() 修改该列表,使其元素按字母顺序排列。打印该列表,核实排列顺序确实变了。

使用sort() 修改该列表,使其元素按与字母顺序相反的顺序排列。打印该列表,核实排列顺序确实变了。

3-9 晚餐嘉宾 :在完成练习3-4~练习3-7时编写的程序之一中,使用len() 打印一条消息,指出你邀请了多少位嘉宾来与你共进晚餐。

3-10 尝试使用各个函数 :想想可存储到列表中的东西,如山岳、河流、国家、城市、语言或你喜欢的任何东西。编写一个程序,在其中创建一个包含这些元素的列

表,然后,对于本章介绍的每个函数,都至少使用一次来处理这个列表。

3.4  使用列表时避免索引错误

刚开始使用列表时,经常会遇到一种错误。假设你有一个包含三个元素的列表,却要求获取第四个元素:

motorcycles = ['honda', 'yamaha', 'suzuki']

print(motorcycles[3])

这将导致索引错误 :

Traceback (most recent call last):

File "motorcycles.py", line 3, in <module>

print(motorcycles[3])

IndexError: list index out of range

Python试图向你提供位于索引3处的元素,但它搜索列表motorcycles 时,却发现索引3处没有元素。鉴于列表索引差一的特征,这种错误很常见。有些人从1开始数,因此以为

第三个元素的索引为3;但在Python中,第三个元素的索引为2,因为索引是从0开始的。

索引错误意味着Python无法理解你指定的索引。程序发生索引错误时,请尝试将你指定的索引减1,然后再次运行程序,看看结果是否正确。

别忘了,每当需要访问最后一个列表元素时,都可使用索引-1 。这在任何情况下都行之有效,即便你最后一次访问列表后,其长度发生了变化:

motorcycles = ['honda', 'yamaha', 'suzuki']

print(motorcycles[-1])

索引-1 总是返回最后一个列表元素,这里为值'suzuki' :

'suzuki'

仅当列表为空时,这种访问最后一个元素的方式才会导致错误:

motorcycles = []

print(motorcycles[-1])

列表motorcycles 不包含任何元素,因此Python返回一条索引错误消息:

Traceback (most recent call last):

File "motorcyles.py", line 3, in <module>

print(motorcycles[-1])

IndexError: list index out of range

注意  发生索引错误却找不到解决办法时,请尝试将列表或其长度打印出来。列表可能与你以为的截然不同,在程序对其进行了动态处理时尤其如此。通过查看列表

或其包含的元素数,可帮助你找出这种逻辑错误。

动手试一试

01 - 图2

3-11 有意引发错误 :如果你还没有在程序中遇到过索引错误,就尝试引发一个这种错误。在你的一个程序中,修改其中的索引,以引发索引错误。关闭程序前,务

必消除这个错误。

3.5  小结

在本章中,你学习了:列表是什么以及如何使用其中的元素;如何定义列表以及如何增删元素;如何对列表进行永久性排序,以及如何为展示列表而进行临时排序;如何确定列

表的长度,以及在使用列表时如何避免索引错误。

在第4章,你将学习如何以更高效的方式处理列表元素。通过使用为数不多的几行代码来遍历列表元素,你就能高效地处理它们,即便列表包含数千乃至数百万个元素。

第 4 章 操作列表

在第3章,你学习了如何创建简单的列表,还学习了如何操作列表元素。在本章中,你将学习如何遍历 整个列表,这只需要几行代码,无论列表有多长。循环让你能

够对列表的每个元素都采取一个或一系列相同的措施,从而高效地处理任何长度的列表,包括包含数千乃至数百万个元素的列表。

4.1  遍历整个列表

你经常需要遍历列表的所有元素,对每个元素执行相同的操作。例如,在游戏中,可能需要将每个界面元素平移相同的距离;对于包含数字的列表,可能需要对每个元素执行相

同的统计运算;在网站中,可能需要显示文章列表中的每个标题。需要对列表中的每个元素都执行相同的操作时,可使用Python中的for 循环。

假设我们有一个魔术师名单,需要将其中每个魔术师的名字都打印出来。为此,我们可以分别获取名单中的每个名字,但这种做法会导致多个问题。例如,如果名单很长,将包

含大量重复的代码。另外,每当名单的长度发生变化时,都必须修改代码。通过使用for 循环,可让Python去处理这些问题。

下面使用for 循环来打印魔术师名单中的所有名字:

magicians.py

❶ magicians = ['alice', 'david', 'carolina']

❷ for magician in magicians:

❸ print(magician)

首先,我们像第3章那样定义了一个列表(见❶)。接下来,我们定义了一个for 循环(见❷);这行代码让Python从列表magicians 中取出一个名字,并将其存储在变

量magician 中。最后,我们让Python打印前面存储到变量magician 中的名字(见❸)。这样,对于列表中的每个名字,Python都将重复执行❷处和❸处的代码行。你可以这

样解读这些代码:对于列表magicians 中的每位魔术师,都将其名字打印出来。输出很简单,就是列表中所有的姓名:

alice

david

carolina

4.1.1  深入地研究循环

循环这种概念很重要,因为它是让计算机自动完成重复工作的常见方式之一。例如,在前面的magicians.py中使用的简单循环中,Python将首先读取其中的第一行代码:

for magician in magicians:

这行代码让Python获取列表magicians 中的第一个值('alice' ),并将其存储到变量magician 中。接下来,Python读取下一行代码:

print(magician)

它让Python打印magician 的值——依然是'alice' 。鉴于该列表还包含其他值,Python返回到循环的第一行:

for magician in magicians:

Python获取列表中的下一个名字——'david' ,并将其存储到变量magician 中,再执行下面这行代码:

print(magician)

Python再次打印变量magician 的值——当前为'david' 。接下来,Python再次执行整个循环,对列表中的最后一个值——'carolina' 进行处理。至此,列表中没有其他的值

了,因此Python接着执行程序的下一行代码。在这个示例中,for 循环后面没有其他的代码,因此程序就此结束。

刚开始使用循环时请牢记,对列表中的每个元素,都将执行循环指定的步骤,而不管列表包含多少个元素。如果列表包含一百万个元素,Python就重复执行指定的步骤一百万次,

且通常速度非常快。

另外,编写for 循环时,对于用于存储列表中每个值的临时变量,可指定任何名称。然而,选择描述单个列表元素的有意义的名称大有帮助。例如,对于小猫列表、小狗列表和

一般性列表,像下面这样编写for 循环的第一行代码是不错的选择:

for cat in cats:

for dog in dogs:

for item in list_of_items:

这些命名约定有助于你明白for 循环中将对每个元素执行的操作。使用单数和复数式名称,可帮助你判断代码段处理的是单个列表元素还是整个列表。

4.1.2  在for 循环中执行更多的操作

在for 循环中,可对每个元素执行任何操作。下面来扩展前面的示例,对于每位魔术师,都打印一条消息,指出他的表演太精彩了。

magicians = ['alice', 'david', 'carolina']

for magician in magicians:

❶ print(magician.title() + ", that was a great trick!")

相比于前一个示例,唯一的不同是对于每位魔术师,都打印了一条以其名字为抬头的消息(见❶)。这个循环第一次迭代时,变量magician 的值为'alice' ,因此Python打印

的第一条消息的抬头为'Alice' 。第二次迭代时,消息的抬头为'David' ,而第三次迭代时,抬头为'Carolina' 。

下面的输出表明,对于列表中的每位魔术师,都打印了一条个性化消息:

Alice, that was a great trick!

David, that was a great trick!

Carolina, that was a great trick!

在for 循环中,想包含多少行代码都可以。在代码行for magician in magicians 后面,每个缩进的代码行都是循环的一部分,且将针对列表中的每个值都执行一次。因

此,可对列表中的每个值执行任意次数的操作。

下面再添加一行代码,告诉每位魔术师,我们期待他的下一次表演:

magicians = ['alice', 'david', 'carolina']

for magician in magicians:

print(magician.title() + ", that was a great trick!")

❶ print("I can't wait to see your next trick, " + magician.title() + ".\n") 由于两条print 语句都缩进了,因此它们都将针对列表中的每位魔术师执行一次。第二条print 语句中的换行符"\n" (见❶)在每次迭代结束后都插入一个空行,从而整洁地

将针对各位魔术师的消息编组:

Alice, that was a great trick!

I can't wait to see your next trick, Alice.

David, that was a great trick!

I can't wait to see your next trick, David.

Carolina, that was a great trick!

I can't wait to see your next trick, Carolina.

在for 循环中,想包含多少行代码都可以。实际上,你会发现使用for 循环对每个元素执行众多不同的操作很有用。

4.1.3  在for 循环结束后执行一些操作

for 循环结束后再怎么做呢?通常,你需要提供总结性输出或接着执行程序必须完成的其他任务。

在for 循环后面,没有缩进的代码都只执行一次,而不会重复执行。下面来打印一条向全体魔术师致谢的消息,感谢他们的精彩表演。想要在打印给各位魔术师的消息后面打印

一条给全体魔术师的致谢消息,需要将相应的代码放在for 循环后面,且不缩进:

magicians = ['alice', 'david', 'carolina']

for magician in magicians:

print(magician.title() + ", that was a great trick!")

print("I can't wait to see your next trick, " + magician.title() + ".\n") ❶ print("Thank you, everyone. That was a great magic show!") 你在前面看到了,开头两条print 语句针对列表中每位魔术师重复执行。然而,由于第三条print 语句没有缩进,因此只执行一次:

Alice, that was a great trick!

I can't wait to see your next trick, Alice.

David, that was a great trick!

I can't wait to see your next trick, David.

Carolina, that was a great trick!

I can't wait to see your next trick, Carolina.

Thank you, everyone. That was a great magic show!

使用for 循环处理数据是一种对数据集执行整体操作的不错的方式。例如,你可能使用for 循环来初始化游戏——遍历角色列表,将每个角色都显示到屏幕上;再在循环后面添

加一个不缩进的代码块,在屏幕上绘制所有角色后显示一个Play Now按钮。

4.2  避免缩进错误

Python根据缩进来判断代码行与前一个代码行的关系。在前面的示例中,向各位魔术师显示消息的代码行是for 循环的一部分,因为它们缩进了。Python通过使用缩进让代码更易

读;简单地说,它要求你使用缩进让代码整洁而结构清晰。在较长的Python程序中,你将看到缩进程度各不相同的代码块,这让你对程序的组织结构有大致的认识。

当你开始编写必须正确缩进的代码时,需要注意一些常见的缩进错误 。例如,有时候,程序员会将不需要缩进的代码块缩进,而对于必须缩进的代码块却忘了缩进。通过查看这

样的错误示例,有助于你以后避开它们,以及在它们出现在程序中时进行修复。

下面来看一些较为常见的缩进错误。

4.2.1  忘记缩进

对于位于for 语句后面且属于循环组成部分的代码行,一定要缩进。如果你忘记缩进,Python会提醒你:

magicians.py

magicians = ['alice', 'david', 'carolina']

for magician in magicians:

❶ print(magician)

print 语句(见❶)应缩进却没有缩进。Python没有找到期望缩进的代码块时,会让你知道哪行代码有问题。

File "magicians.py", line 3

print(magician)

^

IndentationError: expected an indented block

通常,将紧跟在for 语句后面的代码行缩进,可消除这种缩进错误。

4.2.2  忘记缩进额外的代码行

有时候,循环能够运行而不会报告错误,但结果可能会出乎意料。试图在循环中执行多项任务,却忘记缩进其中的一些代码行时,就会出现这种情况。

例如,如果忘记缩进循环中的第2行代码(它告诉每位魔术师,我们期待他的下一次表演),就会出现这种情况:

magicians = ['alice', 'david', 'carolina']

for magician in magicians:

print(magician.title() + ", that was a great trick!")

❶ print("I can't wait to see your next trick, " + magician.title() + ".\n") 第二条print 语句(见❶)原本需要缩进,但Python发现for 语句后面有一行代码是缩进的,因此它没有报告错误。最终的结果是,对于列表中的每位魔术师,都执行了第一

条print 语句,因为它缩进了;而第二条print 语句没有缩进,因此它只在循环结束后执行一次。由于变量magician 的终值为'carolina' ,因此只有她收到了消息“looking forward to the next trick”:

Alice, that was a great trick!

David, that was a great trick!

Carolina, that was a great trick!

I can't wait to see your next trick, Carolina.

这是一个逻辑错误 。从语法上看,这些Python代码是合法的,但由于存在逻辑错误,结果并不符合预期。如果你预期某项操作将针对每个列表元素都执行一次,但它却只执行了

一次,请确定是否需要将一行或多行代码缩进。

4.2.3  不必要的缩进

如果你不小心缩进了无需缩进的代码行,Python将指出这一点:

hello_world.py

message = "Hello Python world!"

❶ print(message) print 语句(见❶)无需缩进,因为它并不属于前一行代码,因此Python将指出这种错误:

File "hello_world.py", line 2

print(message)

^

IndentationError: unexpected indent

为避免意外缩进错误,请只缩进需要缩进的代码。在前面编写的程序中,只有要在for 循环中对每个元素执行的代码需要缩进。

4.2.4  循环后不必要的缩进

如果你不小心缩进了应在循环结束后执行的代码,这些代码将针对每个列表元素重复执行。在有些情况下,这可能导致Python报告语法错误,但在大多数情况下,这只会导致逻辑

错误。

例如,如果不小心缩进了感谢全体魔术师精彩表演的代码行,结果将如何呢?

magicians = ['alice', 'david', 'carolina']

for magician in magicians:

print(magician.title() + ", that was a great trick!")

print("I can't wait to see your next trick, " + magician.title() + ".\n") ❶ print("Thank you everyone, that was a great magic show!") 由于❶处的代码行被缩进,它将针对列表中的每位魔术师执行一次,如❷所示:

Alice, that was a great trick!

I can't wait to see your next trick, Alice.

❷ Thank you everyone, that was a great magic show!

David, that was a great trick!

I can't wait to see your next trick, David.

❷ Thank you everyone, that was a great magic show!

Carolina, that was a great trick!

I can't wait to see your next trick, Carolina.

❷ Thank you everyone, that was a great magic show!

这也是一个逻辑错误,与4.2.2节的错误类似。Python不知道你的本意,只要代码符合语法,它就会运行。如果原本只应执行一次的操作执行了多次,请确定你是否不应该缩进执行

该操作的代码。

4.2.5  遗漏了冒号

for 语句末尾的冒号告诉Python,下一行是循环的第一行。

magicians = ['alice', 'david', 'carolina']

❶ for magician in magicians

print(magician)

如果你不小心遗漏了冒号,如❶所示,将导致语法错误,因为Python不知道你意欲何为。这种错误虽然易于消除,但并不那么容易发现。程序员为找出这样的单字符错误,花费的

时间多得令人惊讶。这样的错误之所以难以发现,是因为通常在我们的意料之外。

动手试一试

4-1 比萨 :想出至少三种你喜欢的比萨,将其名称存储在一个列表中,再使用for 循环将每种比萨的名称都打印出来。

修改这个for 循环,使其打印包含比萨名称的句子,而不仅仅是比萨的名称。对于每种比萨,都显示一行输出,如“I like pepperoni pizza”。

在程序末尾添加一行代码,它不在for 循环中,指出你有多喜欢比萨。输出应包含针对每种比萨的消息,还有一个总结性句子,如“I realy love pizza!”。

4-2 动物 :想出至少三种有共同特征的动物,将这些动物的名称存储在一个列表中,再使用for 循环将每种动物的名称都打印出来。

修改这个程序,使其针对每种动物都打印一个句子,如“A dog would make a great pet”。

在程序末尾添加一行代码,指出这些动物的共同之处,如打印诸如“Any of these animals would make a great pet!”这样的句子。

4.3  创建数值列表

需要存储一组数字的原因有很多,例如,在游戏中,需要跟踪每个角色的位置,还可能需要跟踪玩家的几个最高得分。在数据可视化中,处理的几乎都是由数字(如温度、距

离、人口数量、经度和纬度等)组成的集合。

列表非常适合用于存储数字集合,而Python提供了很多工具,可帮助你高效地处理数字列表。明白如何有效地使用这些工具后,即便列表包含数百万个元素,你编写的代码也能运

行得很好。

4.3.1  使用函数range()

Python函数range() 让你能够轻松地生成一系列的数字。例如,可以像下面这样使用函数range() 来打印一系列的数字:

numbers.py

for value in range(1,5):

print(value)

上述代码好像应该打印数字1~5,但实际上它不会打印数字5:

1

2

3

4

在这个示例中,range() 只是打印数字1~4,这是你在编程语言中经常看到的差一行为的结果。函数range() 让Python从你指定的第一个值开始数,并在到达你指定的第二个值

后停止,因此输出不包含第二个值(这里为5)。

要打印数字1~5,需要使用range(1,6) :

for value in range(1,6):

print(value)

这样,输出将从1开始,到5结束:

1

2

3

4

5

使用range() 时,如果输出不符合预期,请尝试将指定的值加1或减1。

4.3.2  使用range() 创建数字列表

要创建数字列表,可使用函数list() 将range() 的结果直接转换为列表。如果将range() 作为list() 的参数,输出将为一个数字列表。

在前一节的示例中,我们打印了一系列数字。要将这些数字转换为一个列表,可使用list() :

numbers = list(range(1,6))

print(numbers)

结果如下:

[1, 2, 3, 4, 5]

使用函数range() 时,还可指定步长。例如,下面的代码打印1~10内的偶数:

even_numbers.py

even_numbers = list(range(2,11,2))

print(even_numbers)

在这个示例中,函数range() 从2开始数,然后不断地加2,直到达到或超过终值(11),因此输出如下:

[2, 4, 6, 8, 10]

使用函数range() 几乎能够创建任何需要的数字集,例如,如何创建一个列表,其中包含前10个整数(即1~10)的平方呢?在Python中,两个星号(** )表示乘方运算。下面

的代码演示了如何将前10个整数的平方加入到一个列表中:

squares.py

❶ squares = []

❷ for value in range(1,11):

❸ square = value**2

❹ squares.append(square)

❺ print(squares)

首先,我们创建了一个空列表(见❶);接下来,使用函数range() 让Python遍历1~10的值(见❷)。在循环中,计算当前值的平方,并将结果存储到变量square 中(见

❸)。然后,将新计算得到的平方值附加到列表squares 末尾(见❹)。最后,循环结束后,打印列表squares (见❺):

[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

为让这些代码更简洁,可不使用临时变量square ,而直接将每个计算得到的值附加到列表末尾:

squares = []

for value in range(1,11): ❶ squares.append(value**2)

print(squares)

❶处的代码与squares.py中❸处和❹处的代码等效。在循环中,计算每个值的平方,并立即将结果附加到列表squares 的末尾。

创建更复杂的列表时,可使用上述两种方法中的任何一种。有时候,使用临时变量会让代码更易读;而在其他情况下,这样做只会让代码无谓地变长。你首先应该考虑的是,编

写清晰易懂且能完成所需功能的代码;等到审核代码时,再考虑采用更高效的方法。

4.3.3  对数字列表执行简单的统计计算

有几个专门用于处理数字列表的Python函数。例如,你可以轻松地找出数字列表的最大值、最小值和总和:

>>> digits = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]

>>> min(digits)

0

>>> max(digits)

9

>>> sum(digits)

45

注意  出于版面考虑,本节使用的数字列表都很短,但这里介绍的知识也适用于包含数百万个数字的列表。

4.3.4  列表解析

前面介绍的生成列表squares 的方式包含三四行代码,而列表解析让你只需编写一行代码就能生成这样的列表。列表解析 将for 循环和创建新元素的代码合并成一行,并自动

附加新元素。面向初学者的书籍并非都会介绍列表解析,这里之所以介绍列表解析,是因为等你开始阅读他人编写的代码时,很可能会遇到它们。

下面的示例使用列表解析创建你在前面看到的平方数列表:

squares.py

squares = [value**2 for value in range(1,11)]

print(squares)

要使用这种语法,首先指定一个描述性的列表名,如squares ;然后,指定一个左方括号,并定义一个表达式,用于生成你要存储到列表中的值。在这个示例中,表达式

为value**2 ,它计算平方值。接下来,编写一个for 循环,用于给表达式提供值,再加上右方括号。在这个示例中,for 循环为for value in range(1,11) ,它将值

1~10提供给表达式value**2 。请注意,这里的for 语句末尾没有冒号。

结果与你在前面看到的平方数列表相同:

[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

要创建自己的列表解析,需要经过一定的练习,但能够熟练地创建常规列表后,你会发现这样做是完全值得的。当你觉得编写三四行代码来生成列表有点繁复时,就应考虑创建

列表解析了。

动手试一试

4-3 数到20 :使用一个for 循环打印数字1~20(含)。

4-4 一百万 :创建一个列表,其中包含数字1~1 000 000,再使用一个for 循环将这些数字打印出来(如果输出的时间太长,按Ctrl + C停止输出,或关闭输出窗口)。

4-5 计算1~1 000 000 的总和 :创建一个列表,其中包含数字1~1 000 000,再使用min() 和max() 核实该列表确实是从1开始,到1 000 000结束的。另外,对这个列表

调用函数sum() ,看看Python将一百万个数字相加需要多长时间。

4-6 奇数 :通过给函数range() 指定第三个参数来创建一个列表,其中包含1~20的奇数;再使用一个for 循环将这些数字都打印出来。

4-7 3 的倍数 :创建一个列表,其中包含3~30内能被3整除的数字;再使用一个for 循环将这个列表中的数字都打印出来。

4-8 立方 :将同一个数字乘三次称为立方。例如,在Python中,2的立方用2**3 表示。请创建一个列表,其中包含前10个整数(即1~10)的立方,再使用一个for 循

环将这些立方数都打印出来。

4-9 立方解析 :使用列表解析生成一个列表,其中包含前10个整数的立方。

4.4  使用列表的一部分

在第3章中,你学习了如何访问单个列表元素。在本章中,你一直在学习如何处理列表的所有元素。你还可以处理列表的部分元素——Python称之为切片 。

4.4.1  切片

要创建切片,可指定要使用的第一个元素和最后一个元素的索引。与函数range() 一样,Python在到达你指定的第二个索引前面的元素后停止。要输出列表中的前三个元素,需

要指定索引0~3,这将输出分别为0 、1 和2 的元素。

下面的示例处理的是一个运动队成员列表:

players.py

players = ['charles', 'martina', 'michael', 'florence', 'eli']

❶ print(players[0:3])

❶处的代码打印该列表的一个切片,其中只包含三名队员。输出也是一个列表,其中包含前三名队员:

['charles', 'martina', 'michael']

你可以生成列表的任何子集,例如,如果你要提取列表的第2~4个元素,可将起始索引指定为1 ,并将终止索引指定为4 :

players = ['charles', 'martina', 'michael', 'florence', 'eli']

print(players[1:4])

这一次,切片始于'marita' ,终于'florence' :

['martina', 'michael', 'florence']

如果你没有指定第一个索引,Python将自动从列表开头开始:

players = ['charles', 'martina', 'michael', 'florence', 'eli']

print(players[:4])

由于没有指定起始索引,Python从列表开头开始提取:

['charles', 'martina', 'michael', 'florence']

要让切片终止于列表末尾,也可使用类似的语法。例如,如果要提取从第3个元素到列表末尾的所有元素,可将起始索引指定为2 ,并省略终止索引:

players = ['charles', 'martina', 'michael', 'florence', 'eli']

print(players[2:])

Python将返回从第3个元素到列表末尾的所有元素:

['michael', 'florence', 'eli']

无论列表多长,这种语法都能够让你输出从特定位置到列表末尾的所有元素。本书前面说过,负数索引返回离列表末尾相应距离的元素,因此你可以输出列表末尾的任何切片。

例如,如果你要输出名单上的最后三名队员,可使用切片players[-3:] :

players = ['charles', 'martina', 'michael', 'florence', 'eli']

print(players[-3:])

上述代码打印最后三名队员的名字,即便队员名单的长度发生变化,也依然如此。

4.4.2  遍历切片

如果要遍历列表的部分元素,可在for 循环中使用切片。在下面的示例中,我们遍历前三名队员,并打印他们的名字:

players = ['charles', 'martina', 'michael', 'florence', 'eli']

print("Here are the first three players on my team:")

❶ for player in players[:3]:

print(player.title())

❶处的代码没有遍历整个队员列表,而只遍历前三名队员:

Here are the first three players on my team:

Charles

Martina

Michael

在很多情况下,切片都很有用。例如,编写游戏时,你可以在玩家退出游戏时将其最终得分加入到一个列表中。然后,为获取该玩家的三个最高得分,你可以将该列表按降序排

列,再创建一个只包含前三个得分的切片。处理数据时,可使用切片来进行批量处理;编写Web应用程序时,可使用切片来分页显示信息,并在每页显示数量合适的信息。

4.4.3  复制列表

你经常需要根据既有列表创建全新的列表。下面来介绍复制列表的工作原理,以及复制列表可提供极大帮助的一种情形。

要复制列表,可创建一个包含整个列表的切片,方法是同时省略起始索引和终止索引([:] )。这让Python创建一个始于第一个元素,终止于最后一个元素的切片,即复制整个

列表。

例如,假设有一个列表,其中包含你最喜欢的四种食品,而你还想创建另一个列表,在其中包含一位朋友喜欢的所有食品。不过,你喜欢的食品,这位朋友都喜欢,因此你可以

通过复制来创建这个列表:

foods.py

❶ my_foods = ['pizza', 'falafel', 'carrot cake']

❷ friend_foods = my_foods[:]

print("My favorite foods are:")

print(my_foods)

print("\nMy friend's favorite foods are:")

print(friend_foods)

我们首先创建了一个名为my_foods 的食品列表(见❶),然后创建了一个名为friend_foods 的新列表(见❷)。我们在不指定任何索引的情况下从列表my_foods 中提取

一个切片,从而创建了这个列表的副本,再将该副本存储到变量friend_foods 中。打印每个列表后,我们发现它们包含的食品相同:

My favorite foods are:

['pizza', 'falafel', 'carrot cake']

My friend's favorite foods are:

['pizza', 'falafel', 'carrot cake']

为核实我们确实有两个列表,下面在每个列表中都添加一种食品,并核实每个列表都记录了相应人员喜欢的食品:

my_foods = ['pizza', 'falafel', 'carrot cake']

❶ friend_foods = my_foods[:]

❷ my_foods.append('cannoli')

❸ friend_foods.append('ice cream')

print("My favorite foods are:")

print(my_foods)

print("\nMy friend's favorite foods are:")

print(friend_foods)

与前一个示例一样,我们首先将my_foods 的元素复制到新列表friend_foods 中(见❶)。接下来,在每个列表中都添加一种食品:在列表my_foods 中添加'cannoli'

(见❷),而在friend_foods 中添加'ice cream' (见❸)。最后,打印这两个列表,核实这两种食品包含在正确的列表中。

My favorite foods are:

❹ ['pizza', 'falafel', 'carrot cake', 'cannoli']

My friend's favorite foods are:

❺ ['pizza', 'falafel', 'carrot cake', 'ice cream']

❹处的输出表明,'cannoli' 包含在你喜欢的食品列表中,而'ice cream' 没有。❺处的输出表明,'ice cream' 包含在你朋友喜欢的食品列表中,而'cannoli' 没

有。倘若我们只是简单地将my_foods 赋给friend_foods ,就不能得到两个列表。例如,下例演示了在不使用切片的情况下复制列表的情况:

my_foods = ['pizza', 'falafel', 'carrot cake']

这行不通

❶ friend_foods = my_foods

my_foods.append('cannoli')

friend_foods.append('ice cream')

print("My favorite foods are:")

print(my_foods)

print("\nMy friend's favorite foods are:")

print(friend_foods)

这里将my_foods 赋给friend_foods ,而不是将my_foods 的副本存储到friend_foods (见❶)。这种语法实际上是让Python将新变量friend_foods 关联到包含

在my_foods 中的列表,因此这两个变量都指向同一个列表。鉴于此,当我们将'cannoli' 添加到my_foods 中时,它也将出现在friend_foods 中;同样,虽然'ice cream' 好像只被加入到了friend_foods 中,但它也将出现在这两个列表中。

输出表明,两个列表是相同的,这并非我们想要的结果:

My favorite foods are:

['pizza', 'falafel', 'carrot cake', 'cannoli', 'ice cream']

My friend's favorite foods are:

['pizza', 'falafel', 'carrot cake', 'cannoli', 'ice cream']

注意  现在暂时不要考虑这个示例中的细节。基本上,当你试图使用列表的副本时,如果结果出乎意料,请确认你像第一个示例那样使用切片复制了列表。

动手试一试

4-10 切片 :选择你在本章编写的一个程序,在末尾添加几行代码,以完成如下任务。

打印消息“The first three items in the list are:”,再使用切片来打印列表的前三个元素。

打印消息“Three items from the middle of the list are:”,再使用切片来打印列表中间的三个元素。

打印消息“The last three items in the list are:”,再使用切片来打印列表末尾的三个元素。

4-11 你的比萨和我的比萨 :在你为完成练习4-1而编写的程序中,创建比萨列表的副本,并将其存储到变量friend_pizzas 中,再完成如下任务。

在原来的比萨列表中添加一种比萨。

在列表friend_pizzas 中添加另一种比萨。

核实你有两个不同的列表。为此,打印消息“My favorite pizzas are:”,再使用一个for 循环来打印第一个列表;打印消息“My friend's favorite pizzas are:”,再使用一

个for 循环来打印第二个列表。核实新增的比萨被添加到了正确的列表中。

4-12 使用多个循环 :在本节中,为节省篇幅,程序foods.py的每个版本都没有使用for 循环来打印列表。请选择一个版本的foods.py,在其中编写两个for 循环,将各

个食品列表都打印出来。

4.5  元组

列表非常适合用于存储在程序运行期间可能变化的数据集。列表是可以修改的,这对处理网站的用户列表或游戏中的角色列表至关重要。然而,有时候你需要创建一系列不可修

改的元素,元组可以满足这种需求。Python将不能修改的值称为不可变的 ,而不可变的列表被称为元组 。

4.5.1  定义元组

元组看起来犹如列表,但使用圆括号而不是方括号来标识。定义元组后,就可以使用索引来访问其元素,就像访问列表元素一样。

例如,如果有一个大小不应改变的矩形,可将其长度和宽度存储在一个元组中,从而确保它们是不能修改的:

dimensions.py

❶ dimensions = (200, 50)

❷ print(dimensions[0])

print(dimensions[1])

我们首先定义了元组dimensions (见❶),为此我们使用了圆括号而不是方括号。接下来,我们分别打印该元组的各个元素,使用的语法与访问列表元素时使用的语法相同

(见❷):

200

50

下面来尝试修改元组dimensions 中的一个元素,看看结果如何:

dimensions = (200, 50)

❶ dimensions[0] = 250

❶处的代码试图修改第一个元素的值,导致Python返回类型错误消息。由于试图修改元组的操作是被禁止的,因此Python指出不能给元组的元素赋值:

Traceback (most recent call last):

File "dimensions.py", line 3, in <module>

dimensions[0] = 250

TypeError: 'tuple' object does not support item assignment

代码试图修改矩形的尺寸时,Python报告错误,这很好,因为这正是我们希望的。

4.5.2  遍历元组中的所有值

像列表一样,也可以使用for 循环来遍历元组中的所有值:

dimensions = (200, 50)

for dimension in dimensions:

print(dimension)

就像遍历列表时一样,Python返回元组中所有的元素,:

200

50

4.5.3  修改元组变量

虽然不能修改元组的元素,但可以给存储元组的变量赋值。因此,如果要修改前述矩形的尺寸,可重新定义整个元组:

❶ dimensions = (200, 50)

print("Original dimensions:")

for dimension in dimensions:

print(dimension)

❷ dimensions = (400, 100)

❸ print("\nModified dimensions:")

for dimension in dimensions:

print(dimension)

我们首先定义了一个元组,并将其存储的尺寸打印了出来(见❶);接下来,将一个新元组存储到变量dimensions 中(见❷);然后,打印新的尺寸(见❸)。这次,Python 不会报告任何错误,因为给元组变量赋值是合法的:

Original dimensions:

200

50

Modified dimensions:

400

100

相比于列表,元组是更简单的数据结构。如果需要存储的一组值在程序的整个生命周期内都不变,可使用元组。

动手试一试

4-13 自助餐 :有一家自助式餐馆,只提供五种简单的食品。请想出五种简单的食品,并将其存储在一个元组中。

使用一个for 循环将该餐馆提供的五种食品都打印出来。

尝试修改其中的一个元素,核实Python确实会拒绝你这样做。

餐馆调整了菜单,替换了它提供的其中两种食品。请编写一个这样的代码块:给元组变量赋值,并使用一个for 循环将新元组的每个元素都打印出来。

4.6  设置代码格式

随着你编写的程序越来越长,有必要了解一些代码格式设置约定。请花时间让你的代码尽可能易于阅读;让代码易于阅读有助于你掌握程序是做什么的,也可以帮助他人理解你

编写的代码。

为确保所有人编写的代码的结构都大致一致,Python程序员都遵循一些格式设置约定。学会编写整洁的Python后,就能明白他人编写的Python代码的整体结构——只要他们和你遵

循相同的指南。要成为专业程序员,应从现在开始就遵循这些指南,以养成良好的习惯。

4.6.1  格式设置指南

若要提出Python语言修改建议,需要编写Python改进提案 (Python Enhancement Proposal,PEP)。PEP 8是最古老的PEP之一,它向Python程序员提供了代码格式设置指南。PEP 8的

篇幅很长,但大都与复杂的编码结构相关。

Python格式设置指南的编写者深知,代码被阅读的次数比编写的次数多。代码编写出来后,调试时你需要阅读它;给程序添加新功能时,需要花很长的时间阅读代码;与其他程序

员分享代码时,这些程序员也将阅读它们。

如果一定要在让代码易于编写和易于阅读之间做出选择,Python程序员几乎总是会选择后者。下面的指南可帮助你从一开始就编写出清晰的代码。

4.6.2  缩进

PEP 8建议每级缩进都使用四个空格,这既可提高可读性,又留下了足够的多级缩进空间。

在字处理文档中,大家常常使用制表符而不是空格来缩进。对于字处理文档来说,这样做的效果很好,但混合使用制表符和空格会让Python解释器感到迷惑。每款文本编辑器都提

供了一种设置,可将输入的制表符转换为指定数量的空格。你在编写代码时应该使用制表符键,但一定要对编辑器进行设置,使其在文档中插入空格而不是制表符。

在程序中混合使用制表符和空格可能导致极难解决的问题。如果你混合使用了制表符和空格,可将文件中所有的制表符转换为空格,大多数编辑器都提供了这样的功能。

4.6.3  行长

很多Python程序员都建议每行不超过80字符。最初制定这样的指南时,在大多数计算机中,终端窗口每行只能容纳79字符;当前,计算机屏幕每行可容纳的字符数多得多,为何还

要使用79字符的标准行长呢?这里有别的原因。专业程序员通常会在同一个屏幕上打开多个文件,使用标准行长可以让他们在屏幕上并排打开两三个文件时能同时看到各个文件

的完整行。PEP 8还建议注释的行长都不超过72字符,因为有些工具为大型项目自动生成文档时,会在每行注释开头添加格式化字符。

PEP 8中有关行长的指南并非不可逾越的红线,有些小组将最大行长设置为99字符。在学习期间,你不用过多地考虑代码的行长,但别忘了,协作编写程序时,大家几乎都遵守

PEP 8指南。在大多数编辑器中,都可设置一个视觉标志——通常是一条竖线,让你知道不能越过的界线在什么地方。

注意  附录B介绍了如何配置文本编辑器,以使其:在你按制表符键时插入四个空格;显示一条垂直参考线,帮助你遵守行长不能超过79字符的约定。

4.6.4  空行

要将程序的不同部分分开,可使用空行。你应该使用空行来组织程序文件,但也不能滥用;只要按本书的示例展示的那样做,就能掌握其中的平衡。例如,如果你有5行创建列表

的代码,还有3行处理该列表的代码,那么用一个空行将这两部分隔开是合适的。然而,你不应使用三四个空行将它们隔开。

空行不会影响代码的运行,但会影响代码的可读性。Python解释器根据水平缩进情况来解读代码,但不关心垂直间距。

4.6.5  其他格式设置指南

PEP 8还有很多其他的格式设置建议,但这些指南针对的程序大都比目前为止本书提到的程序复杂。等介绍更复杂的Python结构时,我们再来分享相关的PEP 8指南。

动手试一试

4-14 PEP 8 :请访问https://python.org/dev/peps/pep-0008/ ,阅读PEP 8格式设置指南。当前,这些指南适用的不多,但你可以大致浏览一下。

4-15 代码审核 :从本章编写的程序中选择三个,根据PEP 8指南对它们进行修改。

每级缩进都使用四个空格。对你使用的文本编辑器进行设置,使其在你按Tab键时都插入四个空格;如果你还没有这样做,现在就去做吧(有关如何设置,请参

阅附录B)。

每行都不要超过80字符。对你使用的编辑器进行设置,使其在第80个字符处显示一条垂直参考线。

不要在程序文件中过多地使用空行。

4.7  小结

在本章中,你学习了:如何高效地处理列表中的元素;如何使用for 循环遍历列表,Python如何根据缩进来确定程序的结构以及如何避免一些常见的缩进错误;如何创建简单的

数字列表,以及可对数字列表执行的一些操作;如何通过切片来使用列表的一部分和复制列表。你还学习了元组(它对不应变化的值提供了一定程度的保护),以及在代码变得

越来越复杂时如何设置格式,使其易于阅读。

在第5章中,你将学习如何使用if 语句在不同的条件下采取不同的措施;学习如何将一组较复杂的条件测试组合起来,并在满足特定条件时采取相应的措施。你还将学习如何在

遍历列表时,通过使用if 语句对特定元素采取特定的措施。

第 5 章 if 语句

01 - 图3

编程时经常需要检查一系列条件,并据此决定采取什么措施。在Python中,if 语句让你能够检查程序的当前状态,并据此采取相应的措施。

在本章中,你将学习条件测试,以检查感兴趣的任何条件。你将学习简单的if 语句,以及创建一系列复杂的if 语句来确定当前到底处于什么情形。接下来,你将把

学到的知识应用于列表,以编写for 循环,以一种方式处理列表中的大多数元素,并以另一种不同的方式处理包含特定值的元素。

5.1  一个简单示例

下面是一个简短的示例,演示了如何使用if 语句来正确地处理特殊情形。假设你有一个汽车列表,并想将其中每辆汽车的名称打印出来。对于大多数汽车,都应以首字母大写的

方式打印其名称,但对于汽车名'bmw' ,应以全大写的方式打印。下面的代码遍历一个列表,并以首字母大写的方式打印其中的汽车名,但对于汽车名'bmw' ,以全大写的方

式打印:

cars.py

cars = ['audi', 'bmw', 'subaru', 'toyota']

for car in cars:

❶ if car == 'bmw':

print(car.upper())

else:

print(car.title())

这个示例中的循环首先检查当前的汽车名是否是'bmw' (见❶)。如果是,就以全大写的方式打印它;否则就以首字母大写的方式打印:

Audi

BMW

Subaru

Toyota

这个示例涵盖了本章将介绍的很多概念。下面先来介绍可用来在程序中检查条件的测试。

5.2  条件测试

每条if 语句的核心都是一个值为True 或False 的表达式,这种表达式被称为条件测试 。Python根据条件测试的值为True 还是False 来决定是否执行if 语句中的代码。如果

条件测试的值为True ,Python就执行紧跟在if 语句后面的代码;如果为False ,Python就忽略这些代码。

5.2.1  检查是否相等

大多数条件测试都将一个变量的当前值同特定值进行比较。最简单的条件测试检查变量的值是否与特定值相等:

❶ >>> car = 'bmw'

❷ >>> car == 'bmw'

True

我们首先使用一个等号将car 的值设置为'bmw' (见❶),这种做法你已见过很多次。接下来,使用两个等号(== )检查car 的值是否为'bmw' 。这个相等运算符 在它两边

的值相等时返回True ,否则返回False 。在这个示例中,两边的值相等,因此Python返回True 。

如果变量car 的值不是'bmw' ,上述测试将返回False :

❶ >>> car = 'audi'

❷ >>> car == 'bmw'

False

一个等号是陈述;对于❶处的代码,可解读为“将变量car 的值设置为'audi' ”。两个等号是发问;对于❷处的代码,可解读为“变量car 的值是'bmw' 吗?”。大多数编程语言

使用等号的方式都与这里演示的相同。

5.2.2  检查是否相等时不考虑大小写

在Python中检查是否相等时区分大小写,例如,两个大小写不同的值会被视为不相等:

>>> car = 'Audi'

>>> car == 'audi'

False

如果大小写很重要,这种行为有其优点。但如果大小写无关紧要,而只想检查变量的值,可将变量的值转换为小写,再进行比较:

>>> car = 'Audi'

>>> car.lower() == 'audi'

True

无论值'Audi' 的大小写如何,上述测试都将返回True ,因为该测试不区分大小写。函数lower() 不会修改存储在变量car 中的值,因此进行这样的比较时不会影响原来的变

量:

❸ >>> car = 'Audi'

❷ >>> car.lower() == 'audi'

True

❸ >>> car

'Audi'

在❶处,我们将首字母大写的字符串'Audi' 存储在变量car 中;在❷处,我们获取变量car 的值并将其转换为小写,再将结果与字符串'audi' 进行比较。这两个字符串相

同,因此Python返回True 。从❸处的输出可知,这个条件测试并没有影响存储在变量car 中的值。

网站采用类似的方式让用户输入的数据符合特定的格式。例如,网站可能使用类似的测试来确保用户名是独一无二的,而并非只是与另一个用户名的大小写不同。用户提交新的

用户名时,将把它转换为小写,并与所有既有用户名的小写版本进行比较。执行这种检查时,如果已经有用户名'john' (不管大小写如何),则用户提交用户名'John' 时将

遭到拒绝。

5.2.3  检查是否不相等

要判断两个值是否不等,可结合使用惊叹号和等号(!= ),其中的惊叹号表示不 ,在很多编程语言中都如此。

下面再使用一条if 语句来演示如何使用不等运算符。我们将把要求的比萨配料存储在一个变量中,再打印一条消息,指出顾客要求的配料是否是意式小银鱼(anchovies):

toppings.py

requested_topping = 'mushrooms'

❶ if requested_topping != 'anchovies':

print("Hold the anchovies!")

❶处的代码行将requested_topping 的值与'anchovies' 进行比较,如果它们不相等,Python将返回True ,进而执行紧跟在if 语句后面的代码;如果这两个值相

等,Python将返回False ,因此不执行紧跟在if 语句后面的代码。

由于requested_topping 的值不是'anchovies' ,因此执行print 语句:

Hold the anchovies!

你编写的大多数条件表达式都检查两个值是否相等,但有时候检查两个值是否不等的效率更高。

5.2.4  比较数字

检查数值非常简单,例如,下面的代码检查一个人是否是18岁:

>>> age = 18

>>> age == 18

True

你还可以检查两个数字是否不等,例如,下面的代码在提供的答案不正确时打印一条消息:

magic_number.py

answer = 17

❶ if answer != 42:

print("That is not the correct answer. Please try again!")

answer (17 )不是42 ,❶处的条件得到满足,因此缩进的代码块得以执行:

That is not the correct answer. Please try again!

条件语句中可包含各种数学比较,如小于、小于等于、大于、大于等于:

>>> age = 19

>>> age < 21

True

>>> age <= 21

True

>>> age > 21

False

>>> age >= 21

False

在if 语句中可使用各种数学比较,这让你能够直接检查关心的条件。

5.2.5  检查多个条件

你可能想同时检查多个条件,例如,有时候你需要在两个条件都为True 时才执行相应的操作,而有时候你只要求一个条件为True 时就执行相应的操作。在这些情况下,关键

字and 和or 可助你一臂之力。

  1. 使用and 检查多个条件

要检查是否两个条件都为True ,可使用关键字and 将两个条件测试合而为一;如果每个测试都通过了,整个表达式就为True ;如果至少有一个测试没有通过,整个表达式就

为False 。

例如,要检查是否两个人都不小于21岁,可使用下面的测试:

❶ >>> age_0 = 22

>>> age_1 = 18

❷ >>> age_0 >= 21 and age_1 >= 21

False

❸ >>> age_1 = 22

>>> age_0 >= 21 and age_1 >= 21

True

在❶处,我们定义了两个用于存储年龄的变量:age_0 和age_1 。在❷处,我们检查这两个变量是否都大于或等于21;左边的测试通过了,但右边的测试没有通过,因此整个

条件表达式的结果为False 。在❸处,我们将age_1 改为22,这样age_1 的值大于21,因此两个测试都通过了,导致整个条件表达式的结果为True 。

为改善可读性,可将每个测试都分别放在一对括号内,但并非必须这样做。如果你使用括号,测试将类似于下面这样:

(age_0 >= 21) and (age_1 >= 21)

  1. 使用or 检查多个条件

关键字or 也能够让你检查多个条件,但只要至少有一个条件满足,就能通过整个测试。仅当两个测试都没有通过时,使用or 的表达式才为False 。

下面再次检查两个人的年龄,但检查的条件是至少有一个人的年龄不小于21岁:

❶ >>> age_0 = 22

>>> age_1 = 18

❷ >>> age_0 >= 21 or age_1 >= 21

True

❶ >>> age_0 = 18

>>> age_0 >= 21 or age_1 >= 21

False

同样,我们首先定义了两个用于存储年龄的变量(见❶)。由于❷处对age_0 的测试通过了,因此整个表达式的结果为True 。接下来,我们将age_0 减小为18;在❸处的测试

中,两个测试都没有通过,因此整个表达式的结果为False 。

5.2.6  检查特定值是否包含在列表中

有时候,执行操作前必须检查列表是否包含特定的值。例如,结束用户的注册过程前,可能需要检查他提供的用户名是否已包含在用户名列表中。在地图程序中,可能需要检查

用户提交的位置是否包含在已知位置列表中。

要判断特定的值是否已包含在列表中,可使用关键字in 。来看你可能为比萨店编写的一些代码;这些代码首先创建一个列表,其中包含用户点的比萨配料,然后检查特定的配料

是否包含在该列表中。

>>> requested_toppings = ['mushrooms', 'onions', 'pineapple']

❶ >>> 'mushrooms' in requested_toppings

True

❷ >>> 'pepperoni' in requested_toppings

False

在❶处和❷处,关键字in 让Python检查列表requested_toppings 是否包含'mushrooms' 和'pepperoni' 。这种技术很有用,它让你能够在创建一个列表后,轻松地检

查其中是否包含特定的值。

5.2.7  检查特定值是否不包含在列表中

还有些时候,确定特定的值未包含在列表中很重要;在这种情况下,可使用关键字not in 。例如,如果有一个列表,其中包含被禁止在论坛上发表评论的用户,就可在允许用

户提交评论前检查他是否被禁言:

banned_users.py

banned_users = ['andrew', 'carolina', 'david']

user = 'marie'

❶ if user not in banned_users:

print(user.title() + ", you can post a response if you wish.")

❶处的代码行明白易懂:如果user 的值未包含在列表banned_users 中,Python将返回True ,进而执行缩进的代码行。

用户'marie' 未包含在列表banned_users 中,因此她将看到一条邀请她发表评论的消息:

Marie, you can post a response if you wish.

5.2.8  布尔表达式

随着你对编程的了解越来越深入,将遇到术语布尔表达式 ,它不过是条件测试的别名。与条件表达式一样,布尔表达式的结果要么为True ,要么为False 。

布尔值通常用于记录条件,如游戏是否正在运行,或用户是否可以编辑网站的特定内容:

game_active = True

can_edit = False

在跟踪程序状态或程序中重要的条件方面,布尔值提供了一种高效的方式。

动手试一试

5-1 条件测试 :编写一系列条件测试;将每个测试以及你对其结果的预测和实际结果都打印出来。你编写的代码应类似于下面这样:

car = 'subaru'

print("Is car == 'subaru'? I predict True.")

print(car == 'subaru')

print("\nIs car == 'audi'? I predict False.")

print(car == 'audi')

详细研究实际结果,直到你明白了它为何为True 或False 。

创建至少10个测试,且其中结果分别为True 和False 的测试都至少有5个。

5-2 更多的条件测试 :你并非只能创建10个测试。如果你想尝试做更多的比较,可再编写一些测试,并将它们加入到conditional_tests.py中。对于下面列出的各种测

试,至少编写一个结果为True 和False 的测试。

检查两个字符串相等和不等。

使用函数lower() 的测试。

检查两个数字相等、不等、大于、小于、大于等于和小于等于。

使用关键字and 和or 的测试。

测试特定的值是否包含在列表中。

测试特定的值是否未包含在列表中。

5.3  if 语句

理解条件测试后,就可以开始编写if 语句了。if 语句有很多种,选择使用哪种取决于要测试的条件数。前面讨论条件测试时,列举了多个if 语句示例,下面更深入地讨论这个

主题。

5.3.1  简单的if 语句

最简单的if 语句只有一个测试和一个操作:

if conditional_test:

do something

在第1行中,可包含任何条件测试,而在紧跟在测试后面的缩进代码块中,可执行任何操作。如果条件测试的结果为True ,Python就会执行紧跟在if 语句后面的代码;否则

Python将忽略这些代码。

假设有一个表示某人年龄的变量,而你想知道这个人是否够投票的年龄,可使用如下代码:

voting.py

age = 19

❶ if age >= 18:

❷ print("You are old enough to vote!")

在❶处,Python检查变量age 的值是否大于或等于18;答案是肯定的,因此Python执行❷处缩进的print 语句:

You are old enough to vote!

在if 语句中,缩进的作用与for 循环中相同。如果测试通过了,将执行if 语句后面所有缩进的代码行,否则将忽略它们。

在紧跟在if 语句后面的代码块中,可根据需要包含任意数量的代码行。下面在一个人够投票的年龄时再打印一行输出,问他是否登记了:

age = 19

if age >= 18:

print("You are old enough to vote!")

print("Have you registered to vote yet?")

条件测试通过了,而两条print 语句都缩进了,因此它们都将执行:

You are old enough to vote!

Have you registered to vote yet?

如果age 的值小于18,这个程序将不会有任何输出。

5.3.2  if-else 语句

经常需要在条件测试通过了时执行一个操作,并在没有通过时执行另一个操作;在这种情况下,可使用Python提供的if-else 语句。if-else 语句块类似于简单的if 语句,但

其中的else 语句让你能够指定条件测试未通过时要执行的操作。

下面的代码在一个人够投票的年龄时显示与前面相同的消息,同时在这个人不够投票的年龄时也显示一条消息:

age = 17

❶ if age >= 18:

print("You are old enough to vote!")

print("Have you registered to vote yet?")

❷ else:

print("Sorry, you are too young to vote.")

print("Please register to vote as soon as you turn 18!")

如果❶处的条件测试通过了,就执行第一个缩进的print 语句块;如果测试结果为False ,就执行❷处的else 代码块。这次age 小于18,条件测试未通过,因此执行else 代

码块中的代码:

Sorry, you are too young to vote.

Please register to vote as soon as you turn 18!

上述代码之所以可行,是因为只存在两种情形:要么够投票的年龄,要么不够。if-else 结构非常适合用于要让Python执行两种操作之一的情形。在这种简单的if-else 结构

中,总是会执行两个操作中的一个。

5.3.3  if-elif-else 结构

经常需要检查超过两个的情形,为此可使用Python提供的if-elif-else 结构。Python只执行if-elif-else 结构中的一个代码块,它依次检查每个条件测试,直到遇到通过

了的条件测试。测试通过后,Python将执行紧跟在它后面的代码,并跳过余下的测试。

在现实世界中,很多情况下需要考虑的情形都超过两个。例如,来看一个根据年龄段收费的游乐场:

4岁以下免费;

4~18岁收费5美元;

18岁(含)以上收费10美元。

如果只使用一条if 语句,如何确定门票价格呢?下面的代码确定一个人所属的年龄段,并打印一条包含门票价格的消息:

amusement_park.py

age = 12

❶ if age < 4:

print("Your admission cost is $0.")

❷ elif age < 18:

print("Your admission cost is $5.")

❸ else:

print("Your admission cost is $10.")

❶处的if 测试检查一个人是否不满4岁,如果是这样,Python就打印一条合适的消息,并跳过余下的测试。❷处的elif 代码行其实是另一个if 测试,它仅在前面的测试未通过

时才会运行。在这里,我们知道这个人不小于4岁,因为第一个测试未通过。如果这个人未满18岁,Python将打印相应的消息,并跳过else 代码块。如果if 测试和elif 测试都

未通过,Python将运行❸处else 代码块中的代码。

在这个示例中,❶处测试的结果为False ,因此不执行其代码块。然而,第二个测试的结果为True (12小于18),因此将执行其代码块。输出为一个句子,向用户指出了门票

价格:

Your admission cost is $5.

只要年龄超过17岁,前两个测试就都不能通过。在这种情况下,将执行else 代码块,指出门票价格为10美元。

为让代码更简洁,可不在if-elif-else 代码块中打印门票价格,而只在其中设置门票价格,并在它后面添加一条简单的print 语句:

age = 12

if age < 4:

❶ price = 0

elif age < 18:

❷ price = 5

else:

❸ price = 10

❹ print("Your admission cost is $" + str(price) + ".")

❶处、❷处和❸处的代码行像前一个示例那样,根据人的年龄设置变量price 的值。在if-elif-else 结构中设置price 的值后,一条未缩进的print 语句❹会根据这个变

量的值打印一条消息,指出门票的价格。

这些代码的输出与前一个示例相同,但if-elif-else 结构的作用更小,它只确定门票价格,而不是在确定门票价格的同时打印一条消息。除效率更高外,这些修订后的代码

还更容易修改:要调整输出消息的内容,只需修改一条而不是三条print 语句。

5.3.4  使用多个elif 代码块

可根据需要使用任意数量的elif 代码块,例如,假设前述游乐场要给老年人打折,可再添加一个条件测试,判断顾客是否符合打折条件。下面假设对于65岁(含)以上的老

人,可以半价(即5美元)购买门票:

age = 12

if age < 4:

price = 0

elif age < 18:

price = 5

❶ elif age < 65:

price = 10

❷ else:

price = 5

print("Your admission cost is $" + str(price) + ".")

这些代码大都未变。第二个elif 代码块(见❶)通过检查确定年龄不到65岁后,才将门票价格设置为全票价格——10美元。请注意,在else 代码块(见❷)中,必须将所赋的

值改为5,因为仅当年龄超过65(含)时,才会执行这个代码块。

5.3.5  省略else 代码块

Python并不要求if-elif 结构后面必须有else 代码块。在有些情况下,else 代码块很有用;而在其他一些情况下,使用一条elif 语句来处理特定的情形更清晰:

age = 12

if age < 4:

price = 0

elif age < 18:

price = 5

elif age < 65:

price = 10

❶ elif age >= 65:

price = 5

print("Your admission cost is $" + str(price) + ".")

❶处的elif 代码块在顾客的年龄超过65(含)时,将价格设置为5美元,这比使用else 代码块更清晰些。经过这样的修改后,每个代码块都仅在通过了相应的测试时才会执

行。

else 是一条包罗万象的语句,只要不满足任何if 或elif 中的条件测试,其中的代码就会执行,这可能会引入无效甚至恶意的数据。如果知道最终要测试的条件,应考虑使用

一个elif 代码块来代替else 代码块。这样,你就可以肯定,仅当满足相应的条件时,你的代码才会执行。

5.3.6  测试多个条件

if-elif-else 结构功能强大,但仅适合用于只有一个条件满足的情况:遇到通过了的测试后,Python就跳过余下的测试。这种行为很好,效率很高,让你能够测试一个特定的

条件。

然而,有时候必须检查你关心的所有条件。在这种情况下,应使用一系列不包含elif 和else 代码块的简单if 语句。在可能有多个条件为True ,且你需要在每个条件为True 时都采取相应措施时,适合使用这种方法。

下面再来看前面的比萨店示例。如果顾客点了两种配料,就需要确保在其比萨中包含这些配料:

toppings.py

❶ requested_toppings = ['mushrooms', 'extra cheese']

❷ if 'mushrooms' in requested_toppings:

print("Adding mushrooms.")

❸ if 'pepperoni' in requested_toppings:

print("Adding pepperoni.")

❹ if 'extra cheese' in requested_toppings:

print("Adding extra cheese.")

print("\nFinished making your pizza!")

我们首先创建了一个列表,其中包含顾客点的配料(见❶)。❷处的if 语句检查顾客是否点了配料蘑菇('mushrooms' ),如果点了,就打印一条确认消息。❸处检查配料辣

香肠('pepperoni' )的代码也是一个简单的if 语句,而不是elif 或else 语句;因此不管前一个测试是否通过,都将进行这个测试。❹处的代码检查顾客是否要求多加芝

士('extra cheese' );不管前两个测试的结果如何,都会执行这些代码。每当这个程序运行时,都会进行这三个独立的测试。

在这个示例中,会检查每个条件,因此将在比萨中添加蘑菇并多加芝士:

Adding mushrooms.

Adding extra cheese.

Finished making your pizza!

如果像下面这样转而使用if-elif-else 结构,代码将不能正确地运行,因为有一个测试通过后,就会跳过余下的测试:

requested_toppings = ['mushrooms', 'extra cheese']

if 'mushrooms' in requested_toppings:

print("Adding mushrooms.")

elif 'pepperoni' in requested_toppings:

print("Adding pepperoni.")

elif 'extra cheese' in requested_toppings:

print("Adding extra cheese.")

print("\nFinished making your pizza!")

第一个测试检查列表中是否包含'mushrooms' ,它通过了,因此将在比萨中添加蘑菇。然而,Python将跳过if-elif-else 结构中余下的测试,不再检查列表中是否包

含'extra cheese' 和'pepperoni' 。其结果是,将添加顾客点的第一种配料,但不会添加其他的配料:

Adding mushrooms.

Finished making your pizza!

总之,如果你只想执行一个代码块,就使用if-elif-else 结构;如果要运行多个代码块,就使用一系列独立的if 语句。

动手试一试

5-3 外星人颜色#1 :假设在游戏中刚射杀了一个外星人,请创建一个名为alien_color 的变量,并将其设置为'green' 、'yellow' 或'red' 。

编写一条if 语句,检查外星人是否是绿色的;如果是,就打印一条消息,指出玩家获得了5个点。

编写这个程序的两个版本,在一个版本中上述测试通过了,而在另一个版本中未通过(未通过测试时没有输出)。

5-4 外星人颜色#2 :像练习5-3那样设置外星人的颜色,并编写一个if-else 结构。

如果外星人是绿色的,就打印一条消息,指出玩家因射杀该外星人获得了5个点。

如果外星人不是绿色的,就打印一条消息,指出玩家获得了10个点。

编写这个程序的两个版本,在一个版本中执行if 代码块,而在另一个版本中执行else 代码块。

5-5 外星人颜色#3 :将练习5-4中的if-else 结构改为if-elif-else 结构。

如果外星人是绿色的,就打印一条消息,指出玩家获得了5个点。

如果外星人是黄色的,就打印一条消息,指出玩家获得了10个点。

如果外星人是红色的,就打印一条消息,指出玩家获得了15个点。

编写这个程序的三个版本,它们分别在外星人为绿色、黄色和红色时打印一条消息。

5-6 人生的不同阶段 :设置变量age 的值,再编写一个if-elif-else 结构,根据age 的值判断处于人生的哪个阶段。

如果一个人的年龄小于2岁,就打印一条消息,指出他是婴儿。

如果一个人的年龄为2(含)~4岁,就打印一条消息,指出他正蹒跚学步。

如果一个人的年龄为4(含)~13岁,就打印一条消息,指出他是儿童。

如果一个人的年龄为13(含)~20岁,就打印一条消息,指出他是青少年。

如果一个人的年龄为20(含)~65岁,就打印一条消息,指出他是成年人。

如果一个人的年龄超过65(含)岁,就打印一条消息,指出他是老年人。

5-7 喜欢的水果 :创建一个列表,其中包含你喜欢的水果,再编写一系列独立的if 语句,检查列表中是否包含特定的水果。

将该列表命名为favorite_fruits ,并在其中包含三种水果。

编写5条if 语句,每条都检查某种水果是否包含在列表中,如果包含在列表中,就打印一条消息,如“You realy like bananas!”。

5.4  使用if 语句处理列表

通过结合使用if 语句和列表,可完成一些有趣的任务:对列表中特定的值做特殊处理;高效地管理不断变化的情形,如餐馆是否还有特定的食材;证明代码在各种情形下都将按

预期那样运行。

5.4.1  检查特殊元素

本章开头通过一个简单示例演示了如何处理特殊值'bmw' ——它需要采用不同的格式进行打印。既然你对条件测试和if 语句有了大致的认识,下面来进一步研究如何检查列表

中的特殊值,并对其做合适的处理。

继续使用前面的比萨店示例。这家比萨店在制作比萨时,每添加一种配料都打印一条消息。通过创建一个列表,在其中包含顾客点的配料,并使用一个循环来指出添加到比萨中

的配料,可以以极高的效率编写这样的代码:

toppings.py

requested_toppings = ['mushrooms', 'green peppers', 'extra cheese']

for requested_topping in requested_toppings:

print("Adding " + requested_topping + ".")

print("\nFinished making your pizza!")

输出很简单,因为上述代码不过是一个简单的for 循环:

Adding mushrooms.

Adding green peppers.

Adding extra cheese.

Finished making your pizza!

然而,如果比萨店的青椒用完了,该如何处理呢?为妥善地处理这种情况,可在for 循环中包含一条if 语句:

requested_toppings = ['mushrooms', 'green peppers', 'extra cheese']

for requested_topping in requested_toppings:

❶ if requested_topping == 'green peppers':

print("Sorry, we are out of green peppers right now.")

❷ else:

print("Adding " + requested_topping + ".")

print("\nFinished making your pizza!")

这里在比萨中添加每种配料前都进行检查。❶处的代码检查顾客点的是否是青椒,如果是,就显示一条消息,指出不能点青椒的原因。❷处的else 代码块确保其他配料都将添

加到比萨中。

输出表明,妥善地处理了顾客点的每种配料:

Adding mushrooms.

Sorry, we are out of green peppers right now.

Adding extra cheese.

Finished making your pizza!

5.4.2  确定列表不是空的

到目前为止,对于处理的每个列表都做了一个简单的假设,即假设它们都至少包含一个元素。我们马上就要让用户来提供存储在列表中的信息,因此不能再假设循环运行时列表

不是空的。有鉴于此,在运行for 循环前确定列表是否为空很重要。

下面在制作比萨前检查顾客点的配料列表是否为空。如果列表是空的,就向顾客确认他是否要点普通比萨;如果列表不为空,就像前面的示例那样制作比萨:

❶ requested_toppings = []

❷ if requested_toppings:

for requested_topping in requested_toppings:

print("Adding " + requested_topping + ".")

print("\nFinished making your pizza!")

❸ else:

print("Are you sure you want a plain pizza?")

在这里,我们首先创建了一个空列表,其中不包含任何配料(见❶)。在❷处我们进行了简单检查,而不是直接执行for 循环。在if 语句中将列表名用在条件表达式中

时,Python将在列表至少包含一个元素时返回True ,并在列表为空时返回False 。如果requested_toppings 不为空,就运行与前一个示例相同的for 循环;否则,就打印

一条消息,询问顾客是否确实要点不加任何配料的普通比萨(见❸)。

在这里,这个列表为空,因此输出如下——询问顾客是否确实要点普通比萨:

Are you sure you want a plain pizza?

如果这个列表不为空,将显示在比萨中添加的各种配料的输出。

5.4.3  使用多个列表

顾客的要求往往五花八门,在比萨配料方面尤其如此。如果顾客要在比萨中添加炸薯条,该怎么办呢?可使用列表和if 语句来确定能否满足顾客的要求。

来看看在制作比萨前如何拒绝怪异的配料要求。下面的示例定义了两个列表,其中第一个列表包含比萨店供应的配料,而第二个列表包含顾客点的配料。这次对

于requested_toppings 中的每个元素,都检查它是否是比萨店供应的配料,再决定是否在比萨中添加它:

❶ available_toppings = ['mushrooms', 'olives', 'green peppers',

'pepperoni', 'pineapple', 'extra cheese']

❷ requested_toppings = ['mushrooms', 'french fries', 'extra cheese']

❸ for requested_topping in requested_toppings:

❹ if requested_topping in available_toppings:

print("Adding " + requested_topping + ".")

❺ else:

print("Sorry, we don't have " + requested_topping + ".")

print("\nFinished making your pizza!")

在❶处,我们定义了一个列表,其中包含比萨店供应的配料。请注意,如果比萨店供应的配料是固定的,也可使用一个元组来存储它们。在❷处,我们又创建了一个列表,其中

包含顾客点的配料,请注意那个不同寻常的配料——'french fries' 。在❸处,我们遍历顾客点的配料列表。在这个循环中,对于顾客点的每种配料,我们都检查它是否包

含在供应的配料列表中(见❹);如果答案是肯定的,就将其加入到比萨中,否则将运行else 代码块(见❺):打印一条消息,告诉顾客不供应这种配料。

这些代码的输出整洁而详实:

Adding mushrooms.

Sorry, we don't have french fries.

Adding extra cheese.

Finished making your pizza!

通过为数不多的几行代码,我们高效地处理了一种真实的情形!

动手试一试

5-8 以特殊方式跟管理员打招呼 :创建一个至少包含5个用户名的列表,且其中一个用户名为'admin' 。想象你要编写代码,在每位用户登录网站后都打印一条问

候消息。遍历用户名列表,并向每位用户打印一条问候消息。

如果用户名为'admin' ,就打印一条特殊的问候消息,如“Helo admin, would you like to see a status report?”。

否则,打印一条普通的问候消息,如“Helo Eric, thank you for logging in again”。

5-9 处理没有用户的情形 :在为完成练习5-8编写的程序中,添加一条if 语句,检查用户名列表是否为空。

如果为空,就打印消息“We need to find some users!”。

删除列表中的所有用户名,确定将打印正确的消息。

5-10 检查用户名 :按下面的说明编写一个程序,模拟网站确保每位用户的用户名都独一无二的方式。

创建一个至少包含5个用户名的列表,并将其命名为current_users 。

再创建一个包含5个用户名的列表,将其命名为new_users ,并确保其中有一两个用户名也包含在列表current_users 中。

遍历列表new_users ,对于其中的每个用户名,都检查它是否已被使用。如果是这样,就打印一条消息,指出需要输入别的用户名;否则,打印一条消息,指

出这个用户名未被使用。

确保比较时不区分大消息;换句话说,如果用户名'John' 已被使用,应拒绝用户名'JOHN' 。

5-11 序数 :序数表示位置,如1st和2nd。大多数序数都以th结尾,只有1、2和3例外。

在一个列表中存储数字1~9。

遍历这个列表。

在循环中使用一个if-elif-else 结构,以打印每个数字对应的序数。输出内容应为1st 、2nd 、3rd 、4th 、5th 、6th 、7th 、8th 和9th ,但每个序

数都独占一行。

5.5  设置if 语句的格式

本章的每个示例都展示了良好的格式设置习惯。在条件测试的格式设置方面,PEP 8提供的唯一建议是,在诸如== 、>= 和<= 等比较运算符两边各添加一个空格,例如,if age < 4: 要比if age<4: 好。

这样的空格不会影响Python对代码的解读,而只是让代码阅读起来更容易。

动手试一试

5-12 设置if 语句的格式 :审核你在本章编写的程序,确保正确地设置了条件测试的格式。

5-13 自己的想法 :与刚拿起本书时相比,现在你是一名能力更强的程序员了。鉴于你对如何在程序中模拟现实情形有了更深入的认识,你可以考虑使用程序来解决一

些问题。随着编程技能不断提高,你可能想解决一些问题,请将这方面的想法记录下来。想想你可能想编写的游戏、想研究的数据集以及想创建的Web应用程序。

5.6  小结

在本章中,你学习了如何编写结果要么为Ture 要么为False 的条件测试。你学习了如何编写简单的if 语句、if-else 语句和if-elif-else 结构。在程序中,你使用了这

些结构来测试特定的条件,以确定这些条件是否满足。你学习了如何在利用高效的for 循环的同时,以不同于其他元素的方式对特定的列表元素进行处理。你还再次学习了

Python就代码格式方面提出的建议,这可确保即便你编写的程序越来越复杂,其代码依然易于阅读和理解。

在第6章,你将学习Python字典。字典类似于列表,但让你能够将不同的信息关联起来。你将学习如何创建和遍历字典,以及如何将字典同列表和if 语句结合起来使用。学习字典

让你能够模拟更多现实世界的情形。

第 6 章 字典

01 - 图4

在本章中,你将学习能够将相关信息关联起来的Python字典。你将学习如何访问和修改字典中的信息。鉴于字典可存储的信息量几乎不受限制,因此我们会演示如何遍

历字典中的数据。另外,你还将学习存储字典的列表、存储列表的字典和存储字典的字典。

理解字典后,你就能够更准确地为各种真实物体建模。你可以创建一个表示人的字典,然后想在其中存储多少信息就存储多少信息:姓名、年龄、地址、职业以及要

描述的任何方面。你还能够存储任意两种相关的信息,如一系列单词及其含义,一系列人名及其喜欢的数字,以及一系列山脉及其海拔等。

6.1  一个简单的字典

来看一个游戏,其中包含一些外星人,这些外星人的颜色和点数各不相同。下面是一个简单的字典,存储了有关特定外星人的信息:

alien.py

alien_0 = {'color': 'green', 'points': 5}

print(alien_0['color'])

print(alien_0['points'])

字典alien_0 存储了外星人的颜色和点数。使用两条print 语句来访问并打印这些信息,如下所示:

green

5

与大多数编程概念一样,要熟练使用字典,也需要一段时间的练习。使用字典一段时间后,你就会明白为何它们能够高效地模拟现实世界中的情形。

6.2  使用字典

在Python中,字典 是一系列键— 值对 。每个键 都与一个值相关联,你可以使用键来访问与之相关联的值。与键相关联的值可以是数字、字符串、列表乃至字典。事实上,可将

任何Python对象用作字典中的值。

在Python中,字典用放在花括号{} 中的一系列键—值对表示,如前面的示例所示:

alien_0 = {'color': 'green', 'points': 5}

键— 值 对是两个相关联的值。指定键时,Python将返回与之相关联的值。键和值之间用冒号分隔,而键—值对之间用逗号分隔。在字典中,你想存储多少个键—值对都可以。

最简单的字典只有一个键—值对,如下述修改后的字典alien_0 所示:

alien_0 = {'color': 'green'}

这个字典只存储了一项有关alien_0 的信息,具体地说是这个外星人的颜色。在这个字典中,字符串'color' 是一个键,与之相关联的值为'green' 。

6.2.1  访问字典中的值

要获取与键相关联的值,可依次指定字典名和放在方括号内的键,如下所示:

alien_0 = {'color': 'green'}

print(alien_0['color'])

这将返回字典alien_0 中与键'color' 相关联的值:

green

字典中可包含任意数量的键—值对。例如,下面是最初的字典alien_0 ,其中包含两个键—值对:

alien_0 = {'color': 'green', 'points': 5}

现在,你可以访问外星人alien_0 的颜色和点数。如果玩家射杀了这个外星人,你就可以使用下面的代码来确定玩家应获得多少个点:

alien_0 = {'color': 'green', 'points': 5}

❶ new_points = alien_0['points']

❷ print("You just earned " + str(new_points) + " points!")

上述代码首先定义了一个字典,然后从这个字典中获取与键'points' 相关联的值(见❶),并将这个值存储在变量new_points 中。接下来,将这个整数转换为字符串,并

打印一条消息,指出玩家获得了多少个点(见❷):

You just earned 5 points!

如果你在有外星人被射杀时都运行这段代码,就会获取该外星人的点数。

6.2.2  添加键— 值对

字典是一种动态结构,可随时在其中添加键—值对。要添加键—值对,可依次指定字典名、用方括号括起的键和相关联的值。

下面在字典alien_0 中添加两项信息:外星人的 x 坐标和 y 坐标,让我们能够在屏幕的特定位置显示该外星人。我们将这个外星人放在屏幕左边缘,且离屏幕上边缘25像素的地

方。由于屏幕坐标系的原点通常为左上角,因此要将该外星人放在屏幕左边缘,可将 x 坐标设置为0;要将该外星人放在离屏幕顶部25像素的地方,可将 y 坐标设置为25,如下所

示:

alien_0 = {'color': 'green', 'points': 5}

print(alien_0)

❶ alien_0['x_position'] = 0

❷ alien_0['y_position'] = 25

print(alien_0)

我们首先定义了前面一直在使用的字典,然后打印这个字典,以显示其信息快照。在❶处,我们在这个字典中新增了一个键—值对,其中的键为'x_position' ,而值为0 。在

❷处,我们重复这样的操作,但使用的键为'y_position' 。打印修改后的字典时,将看到这两个新增的键—值对:

{'color': 'green', 'points': 5}

{'color': 'green', 'points': 5, 'y_position': 25, 'x_position': 0}

这个字典的最终版本包含四个键—值对,其中原来的两个指定外星人的颜色和点数,而新增的两个指定位置。注意,键—值对的排列顺序与添加顺序不同。Python不关心键—值对

的添加顺序,而只关心键和值之间的关联关系。

6.2.3  先创建一个空字典

有时候,在空字典中添加键—值对是为了方便,而有时候必须这样做。为此,可先使用一对空的花括号定义一个字典,再分行添加各个键—值对。例如,下例演示了如何以这种

方式创建字典alien_0 :

alien_0 = {}

alien_0['color'] = 'green'

alien_0['points'] = 5

print(alien_0)

这里首先定义了空字典alien_0 ,再在其中添加颜色和点数,得到前述示例一直在使用的字典:

{'color': 'green', 'points': 5}

使用字典来存储用户提供的数据或在编写能自动生成大量键—值对的代码时,通常都需要先定义一个空字典。

6.2.4  修改字典中的值

要修改字典中的值,可依次指定字典名、用方括号括起的键以及与该键相关联的新值。例如,假设随着游戏的进行,需要将一个外星人从绿色改为黄色:

alien_0 = {'color': 'green'}

print("The alien is " + alien_0['color'] + ".")

alien_0['color'] = 'yellow'

print("The alien is now " + alien_0['color'] + ".")

我们首先定义了一个表示外星人alien_0 的字典,其中只包含这个外星人的颜色。接下来,我们将与键'color' 相关联的值改为'yellow' 。输出表明,这个外星人确实从绿

色变成了黄色:

The alien is green.

The alien is now yellow.

来看一个更有趣的例子:对一个能够以不同速度移动的外星人的位置进行跟踪。为此,我们将存储该外星人的当前速度,并据此确定该外星人将向右移动多远:

alien_0 = {'x_position': 0, 'y_position': 25, 'speed': 'medium'}

print("Original x-position: " + str(alien_0['x_position']))

向右移动外星人

据外星人当前速度决定将其移动多远

❶ if alien_0['speed'] == 'slow':

x_increment = 1

elif alien_0['speed'] == 'medium':

x_increment = 2

else:

这个外星人的速度一定很快

x_increment = 3

新位置等于老位置加上增量

❷ alien_0['x_position'] = alien_0['x_position'] + x_increment

print("New x-position: " + str(alien_0['x_position']))

我们首先定义了一个外星人,其中包含初始的 x 坐标和 y 坐标,还有速度'medium' 。出于简化考虑,我们省略了颜色和点数,但即便包含这些键-值对,这个示例的工作原理也

不会有任何变化。我们还打印了x_position 的初始值,旨在让用户知道这个外星人向右移动了多远。

在❶处,使用了一个if-elif-else 结构来确定外星人应向右移动多远,并将这个值存储在变量x_increment 中。如果外星人的速度为'slow' ,它将向右移动一个单位;

如果速度为'medium' ,将向右移动两个单位;如果为'fast' ,将向右移动三个单位。确定移动量后,将其与x_position 的当前值相加(见❷),再将结果关联到字典中

的键x_position 。

由于这是一个速度中等的外星人,因此其位置将向右移动两个单位:

Original x-position: 0

New x-position: 2

这种技术很棒:通过修改外星人字典中的值,可改变外星人的行为。例如,要将这个速度中等的外星人变成速度很快的外星人,可添加如下代码行:

alien_0['speed'] = fast

这样,再次运行这些代码时,其中的if-elif-else 结构将把一个更大的值赋给变量x_increment 。

6.2.5  删除键— 值对

对于字典中不再需要的信息,可使用del 语句将相应的键—值对彻底删除。使用del 语句时,必须指定字典名和要删除的键。

例如,下面的代码从字典alien_0 中删除键'points' 及其值:

alien_0 = {'color': 'green', 'points': 5}

print(alien_0)

❶ del alien_0['points']

print(alien_0)

❶处的代码行让Python将键'points' 从字典alien_0 中删除,同时删除与这个键相关联的值。输出表明,键'points' 及其值5 已从字典中删除,但其他键—值对未受影响:

{'color': 'green', 'points': 5}

{'color': 'green'}

注意  删除的键—值对永远消失了。

6.2.6  由类似对象组成的字典

在前面的示例中,字典存储的是一个对象(游戏中的一个外星人)的多种信息,但你也可以使用字典来存储众多对象的同一种信息。例如,假设你要调查很多人,询问他们最喜

欢的编程语言,可使用一个字典来存储这种简单调查的结果,如下所示:

favorite_languages = {

'jen': 'python',

'sarah': 'c',

'edward': 'ruby',

'phil': 'python',

}

正如你看到的,我们将一个较大的字典放在了多行中。其中每个键都是一个被调查者的名字,而每个值都是被调查者喜欢的语言。确定需要使用多行来定义字典时,在输入左花

括号后按回车键,再在下一行缩进四个空格,指定第一个键—值对,并在它后面加上一个逗号。此后你再次按回车键时,文本编辑器将自动缩进后续键—值对,且缩进量与第一

个键—值对相同。

定义好字典后,在最后一个键—值对的下一行添加一个右花括号,并缩进四个空格,使其与字典中的键对齐。另外一种不错的做法是在最后一个键—值对后面也加上逗号,为以

后在下一行添加键—值对做好准备。

注意  对于较长的列表和字典,大多数编辑器都有以类似方式设置其格式的功能。对于较长的字典,还有其他一些可行的格式设置方式,因此在你的编辑器或其他源

代码中,你可能会看到稍微不同的格式设置方式。

给定被调查者的名字,可使用这个字典轻松地获悉他喜欢的语言:

favorite_languages.py

favorite_languages = {

'jen': 'python',

'sarah': 'c',

'edward': 'ruby',

'phil': 'python',

}

❶ print("Sarah's favorite language is " +

❷ favorite_languages['sarah'].title() +

❸ ".")

为获悉Sarah 喜欢的语言,我们使用如下代码:

favorite_languages['sarah']

在print 语句中,我们使用了这种语法(见❷);输出指出了Sarah喜欢的语言:

Sarah's favorite language is C.

这个示例还演示了如何将较长的print 语句分成多行。单词print 比大多数字典名都短,因此让输出的第一部分紧跟在左括号后面是合理的(见❶)。请选择在合适的地方拆

分要打印的内容,并在第一行末尾(见❷)加上一个拼接运算符(+ )。按回车键进入print 语句的后续各行,并使用Tab键将它们对齐并缩进一级。指定要打印的所有内容

后,在print 语句的最后一行末尾加上右括号(见❸)。

动手试一试

6-1 人 :使用一个字典来存储一个熟人的信息,包括名、姓、年龄和居住的城市。该字典应包含键first_name 、last_name 、age 和city 。将存储在该字典中

的每项信息都打印出来。

6-2 喜欢的数字 :使用一个字典来存储一些人喜欢的数字。请想出5个人的名字,并将这些名字用作字典中的键;想出每个人喜欢的一个数字,并将这些数字作为值存

储在字典中。打印每个人的名字和喜欢的数字。为让这个程序更有趣,通过询问朋友确保数据是真实的。

6-3 词汇表 :Python字典可用于模拟现实生活中的字典,但为避免混淆,我们将后者称为词汇表。

想出你在前面学过的5个编程词汇,将它们用作词汇表中的键,并将它们的含义作为值存储在词汇表中。

以整洁的方式打印每个词汇及其含义。为此,你可以先打印词汇,在它后面加上一个冒号,再打印词汇的含义;也可在一行打印词汇,再使用换行符(\n )插

入一个空行,然后在下一行以缩进的方式打印词汇的含义。

6.3  遍历字典

一个Python字典可能只包含几个键—值对,也可能包含数百万个键—值对。鉴于字典可能包含大量的数据,Python支持对字典遍历。字典可用于以各种方式存储信息,因此有多种

遍历字典的方式:可遍历字典的所有键—值对、键或值。

6.3.1  遍历所有的键— 值对

探索各种遍历方法前,先来看一个新字典,它用于存储有关网站用户的信息。下面的字典存储一名用户的用户名、名和姓:

user_0 = {

'username': 'efermi',

'first': 'enrico',

'last': 'fermi',

}

利用本章前面介绍过的知识,可访问user_0 的任何一项信息,但如果要获悉该用户字典中的所有信息,该怎么办呢?可以使用一个for 循环来遍历这个字典:

user.py

user_0 = {

'username': 'efermi',

'first': 'enrico',

'last': 'fermi',

}

❶ for key, value in user_0.items():

❷ print("\nKey: " + key)

❸ print("Value: " + value)

如❶所示,要编写用于遍历字典的for 循环,可声明两个变量,用于存储键—值对中的键和值。对于这两个变量,可使用任何名称。下面的代码使用了简单的变量名,这完全可

行:

for k, v in user_0.items()

for 语句的第二部分包含字典名和方法items() (见❶),它返回一个键—值对列表。接下来,for 循环依次将每个键—值对存储到指定的两个变量中。在前面的示例中,我

们使用这两个变量来打印每个键(见❷)及其相关联的值(见❸)。第一条print 语句中的"\n" 确保在输出每个键—值对前都插入一个空行:

Key: last

Value: fermi

Key: first

Value: enrico

Key: username

Value: efermi

注意,即便遍历字典时,键—值对的返回顺序也与存储顺序不同。Python不关心键—值对的存储顺序,而只跟踪键和值之间的关联关系。

在6.2.6节的示例favorite_languages.py中,字典存储的是不同人的同一种信息;对于类似这样的字典,遍历所有的键—值对很合适。如果遍历字典favorite_languages ,将得到

其中每个人的姓名和喜欢的编程语言。由于其中的键都是人名,而值都是语言,因此我们在循环中使用变量name 和language ,而不是key 和value ,这让人更容易明白循环

的作用:

favorite_languages.py

favorite_languages = {

'jen': 'python',

'sarah': 'c',

'edward': 'ruby',

'phil': 'python',

}

❶ for name, language in favorite_languages.items():

❷ print(name.title() + "'s favorite language is " +

language.title() + ".")

❶处的代码让Python遍历字典中的每个键—值对,并将键存储在变量name 中,而将值存储在变量language 中。这些描述性名称能够让人非常轻松地明白print 语句(见❷)

是做什么的。

仅使用几行代码,我们就将全部调查结果显示出来了:

Jen's favorite language is Python.

Sarah's favorite language is C.

Phil's favorite language is Python.

Edward's favorite language is Ruby.

即便字典存储的是上千乃至上百万人的调查结果,这种循环也管用。

6.3.2  遍历字典中的所有键

在不需要使用字典中的值时,方法keys() 很有用。下面来遍历字典favorite_languages ,并将每个被调查者的名字都打印出来:

favorite_languages = {

'jen': 'python',

'sarah': 'c',

'edward': 'ruby',

'phil': 'python',

}

❶ for name in favorite_languages.keys():

print(name.title())

❶处的代码行让Python提取字典favorite_languages 中的所有键,并依次将它们存储到变量name 中。输出列出了每个被调查者的名字:

Jen

Sarah

Phil

Edward

遍历字典时,会默认遍历所有的键,因此,如果将上述代码中的for name in favorite_languages.keys(): 替换为for name in favorite_languages: ,输出

将不变。

如果显式地使用方法keys() 可让代码更容易理解,你可以选择这样做,但如果你愿意,也可省略它。

在这种循环中,可使用当前键来访问与之相关联的值。下面来打印两条消息,指出两位朋友喜欢的语言。我们像前面一样遍历字典中的名字,但在名字为指定朋友的名字时,打

印一条消息,指出其喜欢的语言:

favorite_languages = {

'jen': 'python',

'sarah': 'c',

'edward': 'ruby',

'phil': 'python',

}

❶ friends = ['phil', 'sarah']

for name in favorite_languages.keys():

print(name.title()) ❷ if name in friends:

print(" Hi " + name.title() +

", I see your favorite language is " +

❸ favorite_languages[name].title() + "!")

在❶处,我们创建了一个列表,其中包含我们要通过打印消息,指出其喜欢的语言的朋友。在循环中,我们打印每个人的名字,并检查当前的名字是否在列表friends 中(见

❷)。如果在列表中,就打印一句特殊的问候语,其中包含这位朋友喜欢的语言。为访问喜欢的语言,我们使用了字典名,并将变量name 的当前值作为键(见❸)。每个人的

名字都会被打印,但只对朋友打印特殊消息:

Edward

Phil

Hi Phil, I see your favorite language is Python!

Sarah

Hi Sarah, I see your favorite language is C!

Jen

你还可以使用keys() 确定某个人是否接受了调查。下面的代码确定Erin是否接受了调查:

favorite_languages = {

'jen': 'python',

'sarah': 'c',

'edward': 'ruby',

'phil': 'python',

}

❶ if 'erin' not in favorite_languages.keys():

print("Erin, please take our poll!")

方法keys() 并非只能用于遍历;实际上,它返回一个列表,其中包含字典中的所有键,因此❶处的代码行只是核实'erin' 是否包含在这个列表中。由于她并不包含在这个列

表中,因此打印一条消息,邀请她参加调查:

Erin, please take our poll!

6.3.3  按顺序遍历字典中的所有键

字典总是明确地记录键和值之间的关联关系,但获取字典的元素时,获取顺序是不可预测的。这不是问题,因为通常你想要的只是获取与键相关联的正确的值。

要以特定的顺序返回元素,一种办法是在for 循环中对返回的键进行排序。为此,可使用函数sorted() 来获得按特定顺序排列的键列表的副本:

favorite_languages = {

'jen': 'python',

'sarah': 'c',

'edward': 'ruby',

'phil': 'python',

}

for name in sorted(favorite_languages.keys()):

print(name.title() + ", thank you for taking the poll.")

这条for 语句类似于其他for 语句,但对方法dictionary.keys() 的结果调用了函数sorted() 。这让Python列出字典中的所有键,并在遍历前对这个列表进行排序。输出

表明,按顺序显示了所有被调查者的名字:

Edward, thank you for taking the poll.

Jen, thank you for taking the poll.

Phil, thank you for taking the poll.

Sarah, thank you for taking the poll.

6.3.4  遍历字典中的所有值

如果你感兴趣的主要是字典包含的值,可使用方法values() ,它返回一个值列表,而不包含任何键。例如,如果我们想获得一个这样的列表,即其中只包含被调查者选择的各

种语言,而不包含被调查者的名字,可以这样做:

favorite_languages = {

'jen': 'python',

'sarah': 'c',

'edward': 'ruby',

'phil': 'python',

}

print("The following languages have been mentioned:")

for language in favorite_languages.values():

print(language.title())

这条for 语句提取字典中的每个值,并将它们依次存储到变量language 中。通过打印这些值,就获得了一个列表,其中包含被调查者选择的各种语言:

The following languages have been mentioned:

Python

C

Python

Ruby

这种做法提取字典中所有的值,而没有考虑是否重复。涉及的值很少时,这也许不是问题,但如果被调查者很多,最终的列表可能包含大量的重复项。为剔除重复项,可使用集

合(set)。集合 类似于列表,但每个元素都必须是独一无二的:

favorite_languages = {

'jen': 'python',

'sarah': 'c',

'edward': 'ruby',

'phil': 'python',

}

print("The following languages have been mentioned:")

❶ for language in set(favorite_languages.values()):

print(language.title())

通过对包含重复元素的列表调用set() ,可让Python找出列表中独一无二的元素,并使用这些元素来创建一个集合。在❶处,我们使用了set() 来提

取favorite_languages.values() 中不同的语言。

结果是一个不重复的列表,其中列出了被调查者提及的所有语言:

The following languages have been mentioned:

Python

C

Ruby

随着你更深入地学习Python,经常会发现它内置的功能可帮助你以希望的方式处理数据。

动手试一试

6-4 词汇表2 :既然你知道了如何遍历字典,现在请整理你为完成练习6-3而编写的代码,将其中的一系列print 语句替换为一个遍历字典中的键和值的循环。确定该

循环正确无误后,再在词汇表中添加5个Python术语。当你再次运行这个程序时,这些新术语及其含义将自动包含在输出中。

6-5 河流 :创建一个字典,在其中存储三条大河流及其流经的国家。其中一个键—值对可能是'nile': 'egypt' 。

使用循环为每条河流打印一条消息,如“The Nile runs through Egypt.”。

使用循环将该字典中每条河流的名字都打印出来。

使用循环将该字典包含的每个国家的名字都打印出来。

6-6 调查 :在6.3.1节编写的程序favorite_languages.py中执行以下操作。

创建一个应该会接受调查的人员名单,其中有些人已包含在字典中,而其他人未包含在字典中。

遍历这个人员名单,对于已参与调查的人,打印一条消息表示感谢。对于还未参与调查的人,打印一条消息邀请他参与调查。

6.4  嵌套

有时候,需要将一系列字典存储在列表中,或将列表作为值存储在字典中,这称为嵌套 。你可以在列表中嵌套字典、在字典中嵌套列表甚至在字典中嵌套字典。正如下面的示例

将演示的,嵌套是一项强大的功能。

6.4.1  字典列表

字典alien_0 包含一个外星人的各种信息,但无法存储第二个外星人的信息,更别说屏幕上全部外星人的信息了。如何管理成群结队的外星人呢?一种办法是创建一个外星人列

表,其中每个外星人都是一个字典,包含有关该外星人的各种信息。例如,下面的代码创建一个包含三个外星人的列表:

aliens.py

alien_0 = {'color': 'green', 'points': 5}

alien_1 = {'color': 'yellow', 'points': 10}

alien_2 = {'color': 'red', 'points': 15}

❶ aliens = [alien_0, alien_1, alien_2]

for alien in aliens:

print(alien)

我们首先创建了三个字典,其中每个字典都表示一个外星人。在❶处,我们将这些字典都放到一个名为aliens 的列表中。最后,我们遍历这个列表,并将每个外星人都打印出

来:

{'color': 'green', 'points': 5}

{'color': 'yellow', 'points': 10}

{'color': 'red', 'points': 15}

更符合现实的情形是,外星人不止三个,且每个外星人都是使用代码自动生成的。在下面的示例中,我们使用range() 生成了30个外星人:

创建一个用于存储外星人的空列表

aliens = []

创建30个绿色的外星人

❶ for alien_number in range(30):

❷ new_alien = {'color': 'green', 'points': 5, 'speed': 'slow'}

❸ aliens.append(new_alien)

显示前五个外星人

❹ for alien in aliens[:5]:

print(alien)

print("…")

显示创建了多少个外星人

❺ print("Total number of aliens: " + str(len(aliens)))

在这个示例中,首先创建了一个空列表,用于存储接下来将创建的所有外星人。在❶处,range() 返回一系列数字,其唯一的用途是告诉Python我们要重复这个循环多少次。每

次执行这个循环时,都创建一个外星人(见❷),并将其附加到列表aliens 末尾(见❸)。在❹处,使用一个切片来打印前五个外星人;在❺处,打印列表的长度,以证明确

实创建了30个外星人:

{'speed': 'slow', 'color': 'green', 'points': 5}

{'speed': 'slow', 'color': 'green', 'points': 5}

{'speed': 'slow', 'color': 'green', 'points': 5}

{'speed': 'slow', 'color': 'green', 'points': 5}

{'speed': 'slow', 'color': 'green', 'points': 5}

Total number of aliens: 30

这些外星人都具有相同的特征,但在Python看来,每个外星人都是独立的,这让我们能够独立地修改每个外星人。

在什么情况下需要处理成群结队的外星人呢?想象一下,可能随着游戏的进行,有些外星人会变色且移动速度会加快。必要时,我们可以使用for 循环和if 语句来修改某些外星

人的颜色。例如,要将前三个外星人修改为黄色的、速度为中等且值10个点,可以这样做:

创建一个用于存储外星人的空列表

aliens = []

创建30个绿色的外星人

for alien_number in range (0,30):

new_alien = {'color': 'green', 'points': 5, 'speed': 'slow'}

aliens.append(new_alien)

for alien in aliens[0:3]:

if alien['color'] == 'green':

alien['color'] = 'yellow'

alien['speed'] = 'medium'

alien['points'] = 10

显示前五个外星人

for alien in aliens[0:5]:

print(alien)

print("…")

鉴于我们要修改前三个外星人,需要遍历一个只包含这些外星人的切片。当前,所有外星人都是绿色的,但情况并非总是如此,因此我们编写了一条if 语句来确保只修改绿色外

星人。如果外星人是绿色的,我们就将其颜色改为'yellow' ,将其速度改为'medium' ,并将其点数改为10 ,如下面的输出所示:

{'speed': 'medium', 'color': 'yellow', 'points': 10}

{'speed': 'medium', 'color': 'yellow', 'points': 10}

{'speed': 'medium', 'color': 'yellow', 'points': 10}

{'speed': 'slow', 'color': 'green', 'points': 5}

{'speed': 'slow', 'color': 'green', 'points': 5}

你可以进一步扩展这个循环,在其中添加一个elif 代码块,将黄色外星人改为移动速度快且值15个点的红色外星人,如下所示(这里只列出了循环,而没有列出整个程序):

for alien in aliens[0:3]:

if alien['color'] == 'green':

alien['color'] = 'yellow'

alien['speed'] = 'medium'

alien['points'] = 10

elif alien['color'] == 'yellow':

alien['color'] = 'red'

alien['speed'] = 'fast'

alien['points'] = 15

经常需要在列表中包含大量的字典,而其中每个字典都包含特定对象的众多信息。例如,你可能需要为网站的每个用户创建一个字典(就像6.3.1节的user.py中那样),并将这些

字典存储在一个名为users 的列表中。在这个列表中,所有字典的结构都相同,因此你可以遍历这个列表,并以相同的方式处理其中的每个字典。

6.4.2  在字典中存储列表

有时候,需要将列表存储在字典中,而不是将字典存储在列表中。例如,你如何描述顾客点的比萨呢?如果使用列表,只能存储要添加的比萨配料;但如果使用字典,就不仅可

在其中包含配料列表,还可包含其他有关比萨的描述。

在下面的示例中,存储了比萨的两方面信息:外皮类型和配料列表。其中的配料列表是一个与键'toppings' 相关联的值。要访问该列表,我们使用字典名和键'toppings'

,就像访问字典中的其他值一样。这将返回一个配料列表,而不是单个值:

pizza.py

存储所点比萨的信息

❶ pizza = {

'crust': 'thick',

'toppings': ['mushrooms', 'extra cheese'],

}

概述所点的比萨

❷ print("You ordered a " + pizza['crust'] + "-crust pizza " +

"with the following toppings:")

❸ for topping in pizza['toppings']:

print("\t" + topping)

我们首先创建了一个字典,其中存储了有关顾客所点比萨的信息(见❶)。在这个字典中,一个键是'crust' ,与之相关联的值是字符串'thick' ;下一个键是'toppings'

,与之相关联的值是一个列表,其中存储了顾客要求添加的所有配料。制作前我们概述了顾客所点的比萨(见❷)。为打印配料,我们编写了一个for 循环(见❸)。为访问配

料列表,我们使用了键'toppings' ,这样Python将从字典中提取配料列表。

下面的输出概述了要制作的比萨:

You ordered a thick-crust pizza with the following toppings: