Python-算法思维初学者完全指南-全-
Python 算法思维初学者完全指南(全)
原文:
zh.annas-archive.org/md5/e679266fa7a24db26eea7852656e18cf译者:飞龙
前言
第一部分
初级知识
第一章
计算机是如何工作的
1.1 简介
在当今社会,几乎每一项任务都需要使用计算机。在学校,学生们使用计算机上网和发送电子邮件。在工作场所,人们使用它们来制作演示文稿、分析数据和与客户沟通。在家里,人们使用计算机来玩游戏、连接到社交网络和与世界各地的人聊天。当然,别忘了智能手机,比如 iPhone。它们也是计算机!
计算机之所以能够执行如此多的不同任务,是因为它们具有可编程的能力。换句话说,计算机可以执行任何程序告诉它做的任务。程序是一组计算机遵循以执行特定任务的语句(通常称为指令或命令)。
程序对于计算机来说是必不可少的,因为没有它们,计算机就只是一个什么都不能做的哑机器。实际上,是程序告诉计算机该做什么以及何时去做。另一方面,程序员或软件开发者是那些设计、创建并经常测试计算机程序的人。
这本书使用 Python 语言向您介绍计算机编程的基本概念。
1.2 什么是硬件?
硬件这个术语指的是构成计算机的所有设备或组件。如果你曾经打开过计算机或笔记本电脑的机箱,你可能已经看到了许多其组件,比如微处理器(CPU)、内存和硬盘。计算机不是一个设备,而是一个由所有设备共同工作的系统。这里讨论了典型计算机系统的基本组件。
►中央处理单元(CPU)
这是计算机执行程序中定义的所有任务的部分(基本的算术、逻辑和输入/输出操作)。
►主存储器(RAM – 随机存取存储器)
这是计算机存储程序(在执行/运行过程中)以及程序正在处理的数据的区域。当你关闭计算机或从墙上拔掉电源插头时,存储在此类内存中的所有程序和数据都会丢失。
►主存储器(ROM – 只读存储器)
ROM 或只读存储器是一种只能由计算机读取(但不能更改)的特殊类型的内存。当计算机关闭时,存储在此类内存中的所有程序和数据都不会丢失。ROM 通常包含制造商的指令以及一个名为引导加载程序的程序,其功能是在电源开启后启动计算机系统的操作。
►二级存储设备
这通常是硬盘或固态硬盘(SSD),有时(但更少)是 CD/DVD 驱动器。与主内存(RAM)相比,这种类型的内存可以在没有电源的情况下保存数据更长时间。然而,存储在此内存中的程序不能直接执行。它们必须转移到更快的主内存。
►输入设备
输入设备是指所有从外部世界收集数据并将其输入计算机以供进一步处理的设备。键盘、鼠标和麦克风都是输入设备。
►输出设备
输出设备是指所有将数据输出到外部世界的设备。显示器(屏幕)和打印机都是输出设备。
1.3 什么是软件?
计算机所做的每一件事都由软件控制。软件分为两大类:系统软件和应用软件。
►系统软件是控制和管理计算机基本操作的程序。例如,系统软件控制计算机的内部操作。它管理所有连接到它的设备,并保存数据、加载数据,并允许其他程序执行。系统软件主要有三种类型:
►操作系统。Windows、Linux、macOS、Android 和 iOS 都是操作系统的例子。
►实用软件。这类软件通常与操作系统一起安装。它用于使计算机尽可能高效地运行。防病毒工具和备份工具被认为是实用软件。
►设备驱动软件。设备驱动程序控制着连接到你的计算机的设备,如鼠标或显卡。设备驱动程序是一个像翻译者一样的程序。它将操作系统的指令翻译成设备可以实际理解的指令。
►应用软件是指你用于日常任务的所有其他程序,例如浏览器、文字处理器、记事本、游戏等等。
1.4 计算机如何执行(运行)程序
当你打开计算机时,主内存(RAM)是完全空的。计算机需要做的第一件事是将操作系统从硬盘传输到主内存。
当操作系统加载到主内存后,你可以执行(运行)任何你喜欢的程序(应用软件)。这通常是通过点击、双击或轻触程序对应的图标来完成的。例如,假设你点击了你最喜欢的文字处理程序的图标。这个动作指示你的计算机将文字处理程序从硬盘复制(或加载)到主内存(RAM),以便 CPU 可以执行它。
程序存储在辅助存储设备上,如硬盘。当你将程序安装到你的计算机上时,程序会被复制到你的硬盘上。然后,当你执行程序时,程序会被复制(加载)到主存储器(RAM)中,并执行该程序的副本。
“运行”和“执行”这两个术语是同义的,可以互换使用。
1.5 编译器和解释器
计算机可以执行用严格定义的计算机语言编写的程序。你不能用自然语言,如英语或希腊语编写程序,因为你的计算机不会理解你!
但计算机实际上理解的是什么?计算机可以理解一种特定的低级语言,称为机器语言。在机器语言中,所有语句(或命令)都是由零和一组成的。以下是一个用机器语言编写的程序示例,该程序计算两个数的和。
0010 0001 0000 0100
0001 0001 0000 0101
0011 0001 0000 0110
0111 0000 0000 0001
惊讶吗?不用担心,你不会用这种方式编写程序。希望没有人再以这种方式编写计算机程序了。如今,所有程序员都用高级语言编写他们的程序,然后他们使用一个特殊的程序将它们翻译成机器语言。
高级语言是一种不限于特定类型计算机的语言。
程序员使用两种类型的程序来执行翻译:编译器和解释器。
编译器是一种将用高级语言编写的语句翻译成单独的机器语言程序的程序。你可以随时执行机器语言程序。翻译后,除非你在高级语言程序中进行更改,否则不需要再次运行编译器。
解释器是一种程序,它同时将用高级语言编写的语句翻译并执行。当解释器读取高级语言程序中的每个单独语句时,它会将其翻译成机器语言代码,然后直接执行。这个过程会为程序中的每个语句重复进行。
1.6 什么是源代码?
程序员用高级语言编写的语句(通常称为指令或命令)被称为源代码,或者简单地称为代码。程序员首先将源代码输入到一个称为代码编辑器的程序中,然后使用编译器将其翻译成机器语言程序,或者使用解释器同时进行翻译和执行。
虽然现在可能看起来不常见,但使用简单的文本编辑器编写程序是完全可能的!
1.7 复习问题:正确/错误
对以下每个陈述选择正确或错误。
1)现代计算机能够执行如此多的不同任务,是因为它们具有编程的能力。
2)计算机可以在没有程序的情况下运行。
3)硬盘是硬件的一个例子。
4)即使没有给计算机供电,数据也可以在主存储器(RAM)中长时间存储。
5)数据存储在主存储器(RAM)中,但程序不是。
6)扬声器是输出设备的一个例子。
7)Windows 和 Linux 是软件的例子。
8)设备驱动程序是硬件的一个例子。
9)媒体播放器是系统软件的一个例子。
10)当你打开计算机时,主存储器(RAM)已经包含了操作系统。
11)当你打开你的文字处理应用程序时,它实际上是从辅助存储设备复制到主存储器(RAM)中。
12)在机器语言中,所有语句(命令)都是零和一的序列。
13)如今,计算机不能理解零和一。
14)如今,软件是用由一和零组成的语言编写的。
15)软件是指计算机的物理组件。
16)编译器和解释器是软件。
17)编译器将源代码翻译成可执行文件。
18)解释器创建机器语言程序。
19)考虑到程序可能被多次执行,在经过解释和执行一次翻译后,解释器的需求变得过时。
20)源代码可以使用简单的文本编辑器编写。
21)源代码可以在不编译或解释的情况下由计算机执行。
22)用机器语言编写的程序需要编译(翻译)。
23)编译器将用高级语言编写的程序翻译。
1.8 复习题:多项选择题
选择以下每个陈述的正确答案。
1)以下哪一项不是计算机硬件?
a)硬盘
b)DVD 光盘
c)声卡
d)主存储器(RAM)
2)以下哪一项不是辅助存储设备?
a)DVD 读取/写入设备
b)固态硬盘(SSD)
c)USB 闪存驱动器
d)RAM
3)以下哪个操作不能由 CPU 执行?
a)将数据传输到主存储器(RAM)。
b)从主存储器(RAM)传输数据。
c)执行算术运算。
d)外科手术。
4)触摸屏是
a)输入设备。
b)输出设备。
c)两者都是
5)以下哪一项不是软件?
a)Windows
b)Linux
c)iOS
d)视频游戏
e)网页浏览器
f)以上所有都是软件。
6)以下哪个陈述是正确的?
a)程序存储在硬盘上。
b)程序存储在 USB 闪存驱动器(USB 棒)上。
c)程序存储在主存储器(RAM)中。
d)以上所有选项都正确。
7)以下哪个陈述是正确的?
a)程序直接从硬盘执行。
b)程序直接从 DVD 光盘执行。
c)程序直接从主存储器(RAM)执行。
d)以上所有选项都正确。
e)以上都不正确。
8)程序员不能使用
a)机器语言。
b)自然语言,如英语、希腊语等。
c)Python。
9)编译器翻译
a)将用机器语言编写的程序转换为高级语言程序。
b)将用自然语言(英语、希腊语等)编写的程序转换为机器语言程序。
c)将用高级计算机语言编写的程序转换为机器语言程序。
d)以上都不是
e)以上都是
10)机器语言是
a)机器之间用来相互通信的语言。
b)由直接由计算机使用的数值指令组成的语言。
c)使用英语单词进行操作的编程语言。
11)在用高级计算机语言编写的程序中,如果两个相同的语句连续出现,解释器
a)首先将第一个翻译并执行,然后翻译第二个并执行。
b)首先翻译第一个,然后翻译第二个,然后执行它们两个。
c)只翻译第一个(因为它们是相同的),然后执行两次。
第二章
Python 和集成开发环境
2.1 什么是 Python?
Python 是一种广泛使用的通用、高级计算机编程语言,允许程序员创建应用程序、网页以及许多其他类型的软件。
尽管官方网站称 Python 是一种编程语言,但 Python 通常被称为脚本语言。当然,事实真相介于两者之间。Python 可以用作脚本语言,也可以用作编程语言!
2.2 脚本和程序之间的区别是什么?
从技术角度讲,脚本被解释执行,而程序被编译执行,但这并不是它们的主要区别。它们之间还有一个更重要的大区别!
使用 JavaScript 或 VBA(应用程序可视化基本)等脚本语言编写的脚本的主要目的是控制另一个应用程序。因此,可以说在某些方面 JavaScript 控制着网络浏览器,而 VBA 控制着 Microsoft^® Office 应用程序,如 MS Word 或 MS Excel。
另一方面,用 Python、C++ 或 Java(仅举几个例子)等编程语言编写的程序独立于任何其他应用程序执行。程序可以在用户希望时独立执行,无需托管应用程序。
Microsoft Office 的宏是用 VBA 编写的脚本。它们的目的在于自动化 Microsoft Office 中的某些功能。
很多人认为 JavaScript 是 Java 的简化版本,但实际上名字的相似性只是巧合。
脚本不能独立执行。它需要托管应用程序才能执行。
2.3 为什么你应该学习 Python
Python 是一种所谓的“高级”计算机语言。Python 的编码风格非常易于理解。它是一种灵活且功能强大的语言,非常适合开发中等规模的应用程序或动态网页。它是教授编程的完美语言,尤其是在入门级别,并且在科学和数值计算中得到广泛应用。
Python 的一个能力是能够与计算机的文件系统交互。Python 可以创建文件、写入文件或从文件中读取内容。它还可以创建目录、复制文件、删除文件、重命名文件,甚至更改文件属性。Python 几乎可以执行与文件系统相关的任何任务,使其甚至适用于系统管理任务。例如,你可以编写一个 Python 程序来备份你的文件,或者一个通过重新格式化内容来处理文本文件的程序。
此外,Python 可以执行系统命令或您系统上安装的任何其他程序。因此,用 C、C++、Java 或任何其他计算机语言编写的和编译的程序都可以从 Python 中执行,并且 Python 可以利用它们的输出。这使得您可以在不花费时间重写旧程序的情况下,为您的 Python 程序添加功能。
已经用 Python 编写了数百万——可能甚至数十亿——行代码,您代码重用的可能性巨大!这就是为什么许多人更喜欢使用 Python 而不是其他任何编程语言的原因。这也是您实际上应该学习 Python 的一个非常好的理由!
2.4 Python 的工作原理
计算机不理解自然语言,如英语或希腊语,因此您需要一种计算机语言,如 Python,才能与它们通信。Python 是一种非常强大的高级计算机语言。Python 解释器(或者实际上是一个编译器和解释器的组合)将 Python 语言转换为计算机实际上能理解的语言,这被称为“机器语言”。
在过去,计算机语言要么使用解释器,要么使用编译器。然而,如今,包括 Python 在内的许多计算机语言都同时使用编译器和解释器。Python 编译器将 Python 语句翻译成字节码语句,并将它们保存到 .pyc 文件中。稍后,当用户想要执行该文件时,解释器(通常称为 Python 虚拟机或 Python VM)将字节码转换为低级机器语言代码,以便在硬件上直接执行。
Python 字节码是由 Python 虚拟机执行的一种机器语言。
一些语言使用两个编译器而不是编译器和解释器。例如,在 C# 中,第一个编译器将 C# 语句翻译成一种称为公共中间语言(CIL)的中间语言。CIL 代码存储在磁盘上的一个可执行文件中,通常具有 .exe 扩展名。稍后,当用户想要执行该文件时,.NET 框架执行即时(JIT)编译,将 CIL 代码转换为低级机器语言代码,以便在硬件上直接执行。
在图 2–1 中,您可以看到用 Python 编写的语句是如何编译成字节码的,以及字节码是如何通过解释来执行的。

图 2–1 使用 Python 虚拟机执行 Python 语句
现在有一些合理的问题:为什么会有这么多麻烦?为什么 Python 和其他语言(如 C#)需要翻译两次?为什么 Python 语句不能直接翻译成低级机器语言代码?答案在于 Python 被设计成一种平台无关的编程语言。这意味着程序只需编写一次,但可以在任何设备上执行,无论其操作系统或架构如何,只要安装了适当的 Python 版本。在过去,程序必须为每个计算机平台重新编译,甚至重写。Python 最大的优点之一就是您只需编写和编译一次程序!在图 2-2 中,您可以看到 Python 编写的语句是如何编译成字节码的,以及字节码是如何在安装了适当 Python 版本的任何平台上执行的。

图 2–2 在不同平台上执行 Python 语句
效率是 Python 语句不直接翻译成低级机器语言代码的另一个原因,因为:
1)可以在中间代码(字节码)上执行小的优化。
2)如果存在与您调用的.py 文件同名的.pyc 文件,Python 将自动执行它。这意味着如果存在.pyc 文件且您没有对源代码进行任何更改,Python 可以通过不重新编译源代码来节省一些时间。
如今,很少有解释器真正逐行直接解释代码。现在几乎所有解释器都使用某种中间表示形式。
要编写和执行 Python 程序,您需要安装两个软件应用:一个 Python 解释器和集成开发环境(IDE)。IDE 是编写 Python 程序所必需的,而 Python 解释器则是执行程序所必需的。
2.5 集成开发环境
集成开发环境,或 IDE,是一种包含程序员编写和测试程序所需的所有基本工具的软件。IDE 通常包含源代码编辑器,并集成了编译器或解释器等工具,以及调试器。IDLE 和 Visual Studio Code 是允许程序员编写、执行和调试源代码的 IDE 示例。
“调试器”是一种帮助程序员找到和纠正许多错误的工具。
2.6 IDLE
IDLE 是一个集成开发环境(IDE),它提供了一个非常适合初学者的简单环境,尤其是在教育环境中。使用 IDLE,新手程序员可以轻松地编写和执行他们的 Python 程序。
在 Windows 中,IDLE 模块默认包含在 Python 安装程序中,并在你遵循下一章(第三章)中概述的步骤时安装。此外,在第九章(第九章)中,你将指导如何使用 IDLE 编写、执行和调试 Python 程序。你还将学习许多有用的技巧和窍门,这些技巧和窍门对于你作为初学者程序员的第一步将非常有价值!
Python 和 IDLE 在许多 Linux 发行版中默认包含,但并非所有发行版都包含。例如,在 Ubuntu Linux 中,Python 默认安装,但你可能需要单独安装 IDLE 模块。
2.7 Microsoft Visual Studio
Microsoft Visual Studio 是一个集成开发环境(IDE),它为许多编程语言提供了一套强大的工具(通过单独安装的扩展),并允许你轻松地为 Android、iOS、macOS、Windows 和云端创建应用程序,以及网站、Web 应用程序和 Web 服务。
Visual Studio 远不止是一个文本编辑器。它可以缩进行、匹配单词和括号,并突出显示编写错误的源代码。它还提供自动代码(IntelliSense^®),这意味着当你键入时,它会显示可能的完成列表。IDE 还提供提示,帮助你分析代码并找到任何潜在的问题。它甚至建议一些简单的解决方案来修复这些问题。你可以使用 Visual Studio 不仅来编写,还可以直接从 IDE 中执行你的程序。
Visual Studio 在全球拥有庞大的用户群,这也是为什么它有这么多不同版本的原因。具体来说,在 Microsoft 的下载页面上,你可以下载:
►在 Windows 上运行的 Visual Studio(社区版、专业版或企业版)
►在 macOS 上运行的 Visual Studio for Mac
►在 Windows、macOS 和 Linux 上运行的 Visual Studio Code
在下一章(第三章)中,你将找到链接,指导你如何在你的计算机上安装和配置所需的一切,例如 Python 解释器、IDLE 或 Visual Studio Code,无论是在 Windows 还是 Linux 上。然后,在第九章(第九章)中,你将发现有关如何使用 IDLE 或 Visual Studio Code 编写、执行和调试 Python 程序的指导。这些说明可在我的网站上找到。此外,你还可以在那里找到许多有用的技巧和窍门,这些技巧和窍门对于你作为初学者程序员的第一步将非常有价值!
第三章
需要安装的软件包
3.1 需要安装的内容
为了本书的目的,你需要在计算机上安装 Python 和一个集成开发环境(IDE)。IDLE 和 Visual Studio Code 是此类 IDE 的例子。选择你打算使用的是你的决定。如果你决定使用 IDLE,那么你可以跳过设置 Visual Studio Code。
如果你不确定选择哪个 IDE(IDLE 或 Visual Studio Code),答案很简单。IDLE 轻量级、简单,适合新手程序员,且非常容易安装。另一方面,Visual Studio Code 更复杂,适合稍微更有经验的程序员,并且需要更多努力来设置。
在 Windows 上,IDLE 与 Python 一同安装,无需进一步配置。
Python 和 IDLE 默认包含在许多 Linux 发行版中,但并非所有。例如,在 Ubuntu Linux 中,Python 默认安装,但你可能需要单独安装 IDLE 模块。
关于如何在 Windows 或 Linux 上设置 Python、IDLE 或 Visual Studio Code 的所有说明都保存在我的网站上,以下地址。这使我能够经常审查它们并保持它们最新。
www.bouraspage.com/python-setup-write-execute-debug
.
如果你发现任何不一致之处,请告知我,我将尽快更新说明。要报告问题,请访问以下地址之一:
www.bouraspage.com/report-errata
.
目前,你只需要安装 Python 和一个 IDE。关于如何在网站上编写、执行和调试 Python 程序的说明在此阶段是不必要的。当你达到第九章时,你将需要这些说明。
第七章:在“基础知识介绍”中复习
复习填字游戏
- 解决以下填字游戏。

横向
-
语句或命令。
-
Windows 就是这样一种系统。
-
一种计算机组件。
-
软件的一个类别。
-
一种输入设备。
-
它是设计计算机程序的人。
-
一种输出设备。
-
杀毒软件就是这样一种软件。
-
在当今社会,几乎每个任务都需要使用这个设备。
-
一种计算机组件。
-
所有这些设备组成了一个计算机。
纵向
-
这些设备也是计算机。
-
由于计算机能够 _____________,因此它们可以执行许多不同的任务。
-
只能读取的特殊内存。
-
一种辅助存储设备。
-
浏览器是这种类型的软件。
-
一种输入设备。
-
一种操作系统。
-
解决以下填字游戏。

横向
-
编程语言的一个类别。
-
在机器语言中,所有语句都是由零和 _______ 组成的。
-
广泛使用的通用高级计算机编程语言。
-
程序员编写的用于解决问题的语句。
-
CPU 执行这些基本操作之一。
-
当您关闭计算机时,所有存储在此类型内存中的数据都会丢失。
-
Python __________ 将字节码转换为低级机器语言代码。
-
一种低级语言。
纵向
-
一种同时翻译和执行用高级语言编写的语句的程序。
-
Python __________ 是由 Python 虚拟机执行的机器语言。
-
一种脚本语言。
-
一组语句。
-
运行一个程序。
-
这款软件控制着连接到您计算机的设备。
-
一种将用高级语言编写的语句翻译成单独机器语言程序的程序。
-
IDLE 就是这样一种软件。
-
解决以下填字游戏。

横向
-
一种可以控制 Microsoft Word 的脚本语言。
-
C#使用的中间语言。
-
一种将用高级计算机语言编写的语句翻译成单独机器语言程序的程序。
-
Python 可以执行 ___________ 命令。
-
脚本需要一种 __________ 应用程序才能执行。
-
一种输入设备。
-
Python 语句不能直接翻译成低级机器语言代码的一个原因。
纵向
-
用 VBA 编写的脚本。
-
Python 的一项能力是能够与计算机的 _______ 系统交互。
-
它向用户显示数据。
-
它执行逻辑操作。
-
Visual Studio Code 就是这样一种软件
复习问题
回答以下问题。
-
什么是硬件?
-
列出典型计算机系统的五个基本组件。
-
“引导加载程序”程序做什么?
-
计算机中实际执行程序的哪个部分?
-
计算机中哪个部分在程序运行时持有程序及其数据?
-
计算机的哪个部分可以长时间存储数据,即使在没有电源的情况下?
-
你怎么称呼从外部世界收集数据并将其输入到计算机的设备?
-
列举一些输入设备的例子。
-
你怎么称呼将数据从计算机输出到外部世界的设备?
-
列举一些输出设备的例子。
-
软件是什么?
-
有多少种软件类别,它们的名称是什么?
-
文字处理程序属于哪一类软件?
-
编译器是什么?
-
解释器是什么?
-
“机器语言”这个术语指的是什么?
-
源代码是什么?
-
Python 是什么?
-
脚本和程序之间的区别是什么?
-
Python 有哪些可能的用途?
-
IDLE 和 Visual Studio 是什么?
第二部分
Python 入门
第四章
基本算法概念简介
4.1 什么是算法?
技术上讲,算法^([1])是一个严格定义的有限序列,由定义良好的语句(通常称为指令或命令)组成,为任何可接受的输入值集(如果有输入)提供问题的解决方案或特定类问题的解决方案。换句话说,算法是解决给定问题的逐步过程。有限意味着算法必须达到终点,不能无限期地运行。
在您的现实生活中,您可以在任何地方找到算法,而不仅仅是计算机科学中。例如,准备吐司或一杯茶的过程可以用算法来表示。为了达到目标,必须遵循特定顺序的某些步骤。
4.2 制作一杯茶的算法
以下是一个制作一杯茶的算法。
1)在杯子中放入一袋茶叶。
2)往壶里加水。
3)在壶中烧开水。
4)将一些沸水倒入杯子中。
5)向杯子中加入牛奶。
6)向杯子中加入糖。
7)搅拌茶。
8)喝茶。
如您所见,必须遵循某些步骤。这些步骤有特定的顺序,尽管某些步骤可以重新排列。例如,步骤 5 和 6 可以颠倒。您可以先加糖,然后加牛奶。
请记住,某些步骤的顺序可能可以改变,但您不能将它们移得太远。例如,您不能将步骤 3(“在壶中烧开水。”)移到算法的末尾,因为您最终会喝到一杯冰茶(而不是一杯热茶),这与您的初始目标完全不同!
4.3 算法的性质
在他的书《计算机程序设计艺术》第一卷《基本算法》中,Donald E. Knuth^([3])断言,一个算法必须满足以下五个属性:
►输入:算法必须从指定的一组输入值中获取。
►输出:算法必须从指定的一组输入值产生输出值。输出值是问题的解决方案。
►有限性:对于任何输入,算法必须在有限步骤后终止。
►确定性:算法的所有步骤都必须精确定义。算法中的每条指令都应该是清晰且无歧义的。算法必须明确描述如何执行计算。确定性的属性确保执行指令的代理将始终知道下一步要执行哪个命令。以下是一些不满足确定性属性的算法示例:
►一个涉及除以零的算法,没有任何检查或保护措施。除以零在数学上是未定义的,而未处理此场景的算法可能导致计算中出现意外的结果或错误。
►一个试图计算负数平方根而不考虑复数的算法。负数的平方根不是一个实数,而是一个复数。如果算法没有正确处理这个问题,它可能会产生无效或不合逻辑的结果。
►效率:它指的是算法在有限的时间内,对于所有可能的合法输入(包括边缘情况)持续且准确地产生有意义且正确结果的能力。算法的步骤必须足够基本,以便例如,使用铅笔和纸的人可以精确地执行它们。
4.4 关于算法,我们可以说得很好。但计算机程序究竟是什么呢?
计算机程序不过是用计算机可以理解的语言(如 Python、Java、C++或 C#)编写的算法。
计算机程序实际上不能帮你泡一杯茶或做一顿饭,尽管算法可以指导你完成这些步骤。然而,程序可以(例如)用来计算一组数字的平均值,或者找出其中的最大值。人工智能程序甚至可以下棋或解决逻辑谜题。
4.5 三方当事人!
在算法中总是涉及三方当事人——编写算法的一方、执行算法的一方以及使用或享受算法的一方。
以准备餐点的算法为例。有人编写算法(食谱书的作者),有人执行它(可能是你的母亲,她根据食谱书的步骤准备餐点),有人使用它(可能是你,你享受着这顿饭)。
现在考虑一个真实的计算机程序。以视频游戏为例。有人用计算机语言编写算法(程序员),有人执行它(通常是笔记本电脑或计算机),有人使用它或与之互动(用户)。
请注意,有时“程序员”和“用户”这两个术语可能会引起歧义。当你编写计算机程序时,你暂时扮演“程序员”的角色,但当你使用自己的程序时,你则扮演“用户”的角色。
4.6 创建算法涉及到的三个主要阶段
一个算法应该由三个阶段组成:数据输入、数据处理和结果输出。这个顺序是特定的,不能改变。
考虑一个寻找三个数字平均值的计算机程序。首先,程序必须提示(询问)用户输入数字(数据输入阶段)。接下来,程序必须计算数字的平均值(数据处理阶段)。最后,程序必须在计算机屏幕上显示结果(结果输出阶段)。
让我们更详细地看看这些阶段。
第一阶段 – 数据输入
1)提示用户输入一个数字。
2)提示用户输入第二个数字。
3)提示用户输入第三个数字。
第二阶段 – 数据处理
4)计算三个数字的和。
5)将和除以 3。
第三阶段 – 结果输出
6)在屏幕上显示结果。
在一些罕见的情况下,输入阶段可能不存在,计算机程序可能只包含两个阶段。例如,考虑一个编写来计算以下和的计算机程序。
1 + 2 + 3 + 4 + 5
在这个例子中,用户必须输入没有任何值,因为计算机程序知道确切要做什么。它必须计算从 1 到 5 的数字之和,然后在用户的屏幕上显示 15 的值。这里显示了两个必需的阶段(数据处理和结果输出)。
第一阶段 – 数据输入
没有事情要做
第二阶段 – 数据处理
1)计算 1 + 2 + 3 + 4 + 5 的和。
第三阶段 – 结果输出
2)在屏幕上显示结果。
然而,如果你想让用户决定那个和的上限呢?如果你想让用户决定是计算 1 到 5 的数字之和还是 1 到 20 的数字之和呢?在这种情况下,程序必须在程序开始时包含一个输入阶段,让用户输入那个上限。一旦用户输入了那个上限,计算机就可以计算出结果。这里显示了三个必需的阶段。
第一阶段 – 数据输入
1)提示用户输入一个数字。
第二阶段 – 数据处理
2)计算和 1 + 2 + …(直到用户输入的上限,包括上限)。
第三阶段 – 结果输出
3)在屏幕上显示结果。
例如,如果用户将数字 6 作为上限输入,计算机将找到 1 + 2 + 3 + 4 + 5 + 6 的结果。
4.7 流程图
流程图是一种图形化展示算法的方法,通常在纸上进行。它是算法执行流程的视觉表示。换句话说,它以视觉方式表示执行流程如何从一个语句继续到下一个语句,直到算法结束。流程图使用的基本符号如表 4-1 所示。
| 流程图符号 | 描述 |
|---|---|
.![]() |
开始/结束:表示算法的开始或结束。开始符号有一个出口,结束符号有一个入口。 |
.![]() |
箭头:显示执行流程。从一个符号开始并结束在另一个符号上的箭头表示控制传递到箭头指向的符号。箭头总是以直线形式向上、向下或横向绘制(永远不会以角度绘制)。 |
.![]() |
处理:表示一个过程或数学(公式)计算。处理符号有一个入口和一个出口。 |
.![]() |
数据输入/输出:表示数据输入或结果输出。在大多数情况下,数据来自键盘,结果显示在屏幕上。数据输入/输出符号有一个入口和一个出口。 |
![]() |
决策:表示做出决策的点。基于给定的条件(可以是真或假),算法将遵循右或左的路径。决策符号有一个入口和两个(并且始终只有两个)出口。 |
![]() |
确定循环:表示一个语句或语句块重复预定义的次数。确定循环符号有一个入口和一个出口。 |
![]() |
离页连接符:表示流程图在另一页的延续。当流程图太大而无法适应一张纸时,它们用于连接多页上的段。离页连接符符号有一个入口,而进入页连接符符号有一个出口。 |
表 4-1 流程图符号及其功能
图 4–1 展示了流程图的示例。算法提示用户输入三个数字,然后计算它们的平均值并在计算机屏幕上显示。

图 4–1 计算并显示三个数字平均值的算法流程图
流程图始终以 Start/End 符号开始和结束!
练习 4.7-1 找到三个数字的平均值
设计一个算法,用于计算三个数字的平均值。每当平均值低于 10 时,必须显示消息“Fail!”。否则,如果平均值是 10 或以上,必须显示消息“Pass!”。
解决方案
在这个问题中,必须显示两个不同的消息,但每次算法执行时只能出现一个;消息的措辞取决于平均值。算法的流程图在此展示。

为了节省纸张,你可以提示用户使用一个单独的斜平行四边形输入所有三个数字。
决策符号始终有一个入口和两个出口路径!
当然,很快你就可以开始创建自己的算法了。这个特定的练习相当简单,在本章中作为例外,仅用于演示目的。在开始创建自己的算法或甚至 Python 程序之前,你需要学习更多。只需耐心等待!在接下来的几章中,重大时刻将会到来!
4.8 什么是“保留字”?
在计算机语言中,保留字(或关键字)是一个具有严格预定义意义的词——它被保留用于特殊用途,不能用于其他任何目的。例如,流程图中的 Start、End、Read 和 Write 这些词具有预定义的意义。它们分别用于表示开始、结束、数据输入和结果输出。
所有高级计算机语言都存在保留字。在 Python 中,有诸如 if、while、else 和 for 等许多保留字。然而,每种语言都有自己的保留字集合。例如,C++中的保留字 else if 在 Python 中写作 elif。
4.9 陈述和命令的区别是什么?
关于是否存在陈述和命令之间的区别,互联网上存在很大的讨论。有些人喜欢使用“陈述”这个词,而有些人则使用“命令”。对于初学者程序员来说,两者没有区别;两者都是对计算机的指令!
4.10 什么是结构化编程?
结构化编程的概念于 1966 年由 Corrado Böhm^([4])和 Giuseppe Jacopini^([5])正式化。他们通过序列、决策和迭代展示了理论计算机程序设计。
4.11 三种基本控制结构
结构化编程中有三种基本控制结构。
►顺序控制结构:这指的是逐行执行,其中语句按其在程序中出现的顺序依次执行,不跳过任何一条。它也被称为顺序控制结构。
►决策控制结构:根据条件是真是假,决策控制结构可能会跳过执行整个语句块,甚至执行一个语句块而不是另一个。它也被称为选择控制结构。
►循环控制结构:这是一种允许执行一个语句块多次,直到满足指定条件为止的控制结构。它也被称为迭代控制结构或重复控制结构。
世界上所有的计算机程序都是基于这三种控制结构编写的!
练习 4.11-1 使用流程图理解控制结构
使用流程图,为每种控制结构提供一个示例。
解答
顺序控制结构示例
.
决策控制结构示例

循环控制结构示例

如果你没有完全理解这三种控制结构的深层含义,不要担心,因为接下来的章节将非常彻底地分析它们。耐心是一种美德。你现在需要做的就是等待!
4.12 你的第一个 Python 程序
将流程图转换为如 Python 这样的计算机语言将产生一个 Python 程序。Python 程序不过是一个包含 Python 语句的文本文件。你甚至可以在你的文本编辑器应用程序中编写 Python 程序!不过,请记住,使用 IDLE 或 Visual Studio Code 编写 Python 程序是一个更好的解决方案,因为它们包含的所有功能都可以使你的生活变得更轻松。
Python 源代码以默认的.py 文件扩展名保存在您的硬盘上。
这里有一个非常简单的算法,它在屏幕上显示三条消息。
.
这里是同样的算法,以 Python 程序的形式编写。
print("Hello World!")
print("Hallo Welt!")
print("The End")
4.13 语法错误、逻辑错误和运行时错误之间的区别是什么?
当编写或执行高级语言代码时,可能会发生三种类型的错误:语法错误、逻辑错误和运行时错误。
语法错误是拼写错误、缺少标点符号或缺少闭合括号等错误。语法错误由编译器或解释器检测。如果你尝试执行包含语法错误的 Python 程序,你会在屏幕上收到错误消息,程序将不会执行。你必须纠正任何错误,然后再次尝试执行程序。
一些 IDE,如 Visual Studio Code,会在你输入时检测这些错误,并用波浪形红线下划线标记错误的语句。
逻辑错误是阻止你的程序执行你期望它执行的操作的错误。在逻辑错误中,你根本不会收到任何警告。你的代码可以编译并运行,但结果并不是预期的。逻辑错误很难检测。你必须彻底审查你的程序,以找出错误所在。例如,考虑一个 Python 程序,该程序提示用户输入三个数字,然后计算并显示它们的平均值。然而,在这个程序中,程序员犯了一个打字错误(一个“typo”);他们的一个语句将三个数字的总和除以 5,而不是应该的 3。当然,Python 程序会像往常一样执行,没有任何错误消息,提示用户输入三个数字并显示一个结果,但显然不是正确的结果!找出并纠正错误编写的 Python 语句是程序员的职责,而不是计算机、解释器或编译器!毕竟,计算机并不那么聪明!
运行时错误是在程序执行过程中发生的错误。运行时错误可能导致程序突然终止,甚至导致系统关闭。这类错误是最难检测的。在执行程序之前,无法确定是否会发生此类错误。尽管如此,你可以怀疑它可能会发生!例如,内存不足或除以零会导致运行时错误。
逻辑错误可能是运行时错误的原因!
逻辑错误和运行时错误通常被称为“bug”,通常在软件发布前的调试过程中被发现。当软件发布后发现问题,程序员通常会发布补丁或小更新来修复错误。
4.14 “调试”是什么意思
调试是寻找和减少计算机程序中缺陷(错误)的过程,以便使其按预期运行。
关于“调试”一词起源有一个神话。1940 年,当 Grace Hopper^([6])在哈佛大学研究 Mark II 计算机时,她的同事发现了一只虫子(一只蛾)卡在继电器(一个电动开关)中。这个虫子阻碍了 Mark II 计算机的正常运行。因此,当她的同事试图移除虫子时,Grace Hopper 评论说他们正在“调试”系统!
4.15 注释你的代码
当你编写一个简单易懂的程序时,任何人都可以通过逐行阅读来理解它是如何工作的。然而,长程序很难理解,有时即使是编写程序的人也难以理解。
注释是可以在程序中包含的额外信息,可以使程序更容易阅读和理解。使用注释,你可以添加解释和其他信息,包括:
►谁编写了程序
►程序创建或最后修改的时间
►程序做什么
►程序是如何工作的
注释是为了方便人类读者阅读。编译器和解释器会忽略你程序中添加的任何注释。
然而,你不应该过度注释。没有必要解释程序中的每一行。只有当程序中的某个特定部分难以理解时,才添加注释。
在 Python 中,你可以使用哈希字符(#)添加注释,如下所示。
由 Aristides S. Bouras 创建
创建日期:2003 年 12 月 25 日
修改日期:2008 年 4 月 3 日
描述:此程序在屏幕上显示一些信息
print("Hello Zeus!") #它在屏幕上显示一条信息
在屏幕上显示第二条信息
print("Hello Hera!")
这是一个注释 print("The End")
正如你在前面的程序中看到的,你可以在语句上方或语句末尾添加注释,但不能在语句前方添加。看看最后一个语句,它本应显示信息“结束”。这个语句永远不会被执行,因为它被认为是注释的一部分。
当程序运行时,注释对程序的用户是不可见的。
4.16 用户友好的程序
什么是用户友好的程序?它是用户认为它是朋友而不是敌人的程序,对新手用户来说很容易使用。
如果你想要编写用户友好的程序,你必须站在用户的角度思考。用户希望电脑按照他们的方式工作,以最小的努力完成工作。隐藏的菜单、不清晰的标签和指示,以及误导性的错误信息都可能使程序变得不友好!
最佳定义用户友好设计的定律是“最小惊讶定律”:程序应该以最不令用户惊讶的方式行事。这个定律也通常被称为最小惊讶原则(POLA)。
4.17 复习问题:对/错
对以下每个陈述选择对或错。
1)一份食谱实际上是一个算法。
2)算法仅在计算机科学中使用。
3)一个算法可以无限期地运行。
4)在算法中,你可以将任何步骤移动到任何你希望的位置。
5)算法必须为至少一组输入值产生正确的输出值。
6)计算机可以下棋。
7)一个算法总能成为一个计算机程序。
8)编程是创建计算机程序的过程。
9)在计算机程序中,总是涉及三方:程序员、计算机和用户。
10)程序员和用户有时可以是同一个人。
11)计算机程序可能不会输出任何结果。
12)流程图是一种计算机程序。
13)流程图由一组几何形状组成。
14)流程图是表示算法的一种方法。
15)为了表示一个算法,你可以设计一个流程图而不使用任何开始/结束符号。
16)你可以设计一个流程图而不使用任何流程符号。
17)你可以设计一个流程图而不使用任何数据输入/输出符号。
18)流程图必须始终包含至少一个决策符号。
19)在流程图中,决策符号可以有一个、两个或三个出口路径,具体取决于给定的问题。
20)保留字是所有具有严格预定义意义的单词。
21)结构化编程包括结构化设计。
22)Python 是一种结构化计算机语言。
23)结构化编程的基本原则是它只包括四种基本控制结构。
24)一条写十遍的语句被认为是循环控制结构。
25)决策控制结构指的是逐行执行。
26)拼写错误的密钥词被视为逻辑错误。
27)即使 Python 程序包含逻辑错误,它也可以执行。
28)如果你在 Python 语句的末尾留下感叹号,它被视为语法错误。
29)如果你在 Python 语句的末尾留下感叹号,它不能阻止整个 Python 程序执行。
30)结构化编程的一个优点是在编写计算机程序时不会出错。
31)逻辑错误在编译期间被捕获。
32)运行时错误在编译期间被捕获。
33)语法错误是最难检测的错误。
34)一个计算三角形面积但输出错误结果的程序包含逻辑错误。
35)当程序不包含输出语句时,它包含语法错误。
36)一个程序必须始终包含注释。
37)如果你在程序中添加注释,计算机可以更容易地理解它。
38)你无法在语句上方添加注释。
39)注释对程序的用户是不可见的。
40)如果一个程序可以被新手用户轻松使用,那么它被称为用户友好型。
41)缩写词 POLA 代表“最小娱乐原则”。
4.18 复习题:多项选择题
选择以下每个陈述的正确答案。
1)算法是一系列严格定义的有限序列的明确陈述,它提供了
a)一个问题。
b)一类特定的问题。
c)以上两者都是正确的。
2)以下哪项不是算法必须满足的特性?
a)有效性
b)适宜性
c)确定性
d)输入
3)计算机程序是
a)一个算法。
b)一系列指令。
c)以上两者都是
4)当有人编写一个食谱时,他们是
a)“程序员”
b)“用户”
c)以上都不是
5)以下哪项不属于创建算法涉及到的三个主要阶段之一?
a)数据保护
b)数据输入
c)结果输出
d)数据处理
6)流程图可以是
a)展示在一张纸上。
b)直接输入到计算机中。
c)以上两者都是
7)流程图中的矩形表示
a)输入/输出操作。
b)处理操作。
c)一个决策。
d)以上都不是
8)以下哪项是/是控制结构?
a)一个决策
b)一个序列
c)一个循环
d)以上所有都是控制结构。
9)以下哪个 Python 语句包含语法错误?
a)print(Hello Poseidon)
b)print("It's me! I contain a syntax error!!!")
c)print("Hello Athena")
d)以上都不是
10)以下哪个打印语句实际上被执行了?
a)print("Hello Apollo)
b)#print("Hello Artemis")
c)#This will be executed print("Hello Ares")
d)print("Hello Aphrodite") #This will be executed
e)以上都不是
第五章
变量和常量
5.1 什么是变量?
在计算机科学中,变量是计算机主内存(RAM)中的一个位置,程序可以在此处存储一个值,并在程序执行过程中更改它。
将变量想象成一个透明的盒子,你可以一次插入并保存一个东西。因为盒子是透明的,所以你还可以看到它里面有什么。此外,如果你有两个或更多的盒子,你可以给每个盒子一个独特的名字。例如,你可以有三个盒子,每个盒子包含不同的数字,你可以将这些盒子命名为 numberA、numberB 和 numberC。

示例中名为 numberA、numberB 和 numberC 的框分别包含数字 13、8 和 4。当然,你可以在任何时候检查或甚至更改这些框中包含的值。
现在,假设有人要求你找出前两个框中值的总和,然后将结果存储在最后一个框中。你必须遵循以下步骤:
1)查看前两个框,并检查它们包含的值。
2)使用你的 CPU(这是你的大脑)来计算总和(结果)。
3)将结果(即 21 的值)插入最后一个框中。然而,由于每个框一次只能包含一个单一值,因此值 4 实际上被数字 21 所替换。
现在框看起来是这样的。

在流程图中,将值存储在变量中的动作用左箭头表示
.
这个动作通常表示为“将值或表达式的结果赋给变量”。左箭头被称为值赋值运算符。
请注意,这个箭头始终指向左边。不允许使用右箭头。此外,箭头的左边只能存在一个单一变量。
在实际的计算机科学中,这三个盒子实际上是主内存(RAM)中的三个独立区域,分别命名为 numberA、numberB 和 numberC。

当程序指示 CPU 执行以下语句时
numberC ← numberA + numberB
它遵循与上一个示例相同的三个步骤过程。
1)数字 13 和 8 从名为 numberA 和 numberB 的 RAM 区域转移到 CPU。
(这是第一步,你在其中检查了前两个框中的值)。
2)CPU 计算 13 + 8 的和。
(这是第二步,你在其中使用你的大脑来计算总和或结果)。
3)结果 21 从 CPU 转移到 RAM 的名为 numberC 的区域,替换了现有的数字 4。
(这是第三步,你在其中将结果插入最后一个框)。
执行后,RAM 看起来是这样的。
.
当 Python 程序运行时,一个变量可以持有各种值,但一次只能持有一个值。当你将一个值赋给一个变量时,这个值会一直存储,直到你分配一个新的值来替换旧的值。
变量的内容可以改变为不同的值,但它的名称将始终相同,因为名称只是内存中位置的标识符。
变量是计算机科学中最重要元素之一,因为它帮助你与存储在主内存(RAM)中的数据进行交互。很快,你将学习如何在 Python 中使用变量。
5.2 什么是常量?
有时候,你可能需要使用在程序运行期间不能改变的价值。这样的值被称为常量。简单来说,常量可以被视为一个锁定的变量。这意味着当程序开始运行时,一个值被分配给常量,之后,在程序运行过程中,没有任何东西可以改变这个值。例如,在一个金融程序中,利率可以被声明为一个常量。
一个常量的描述性名称也可以提高你程序的可读性,并帮助你避免一些错误。例如,假设你在程序中的许多地方都使用了值 3.14159265(但不是作为常量)。如果你在输入这个数字时犯了打字错误,这将产生错误的结果。但是,如果这个值被赋予一个名称,任何在名称上的打字错误都会被编译器检测到,并且你会收到一个错误信息。
不幸的是,Python 不支持常量。你可以用一个变量来代替常量,但要注意不要在程序中使用这个变量时意外地改变它的初始值。
在流程图中,你可以用等号(=)表示将常量设置为值的动作。

这本书使用大写字母来区分常量和变量。
考虑一个算法,允许用户输入三个不同产品的价格,然后计算并显示每个产品的 20%增值税(简称 VAT)。图 5-1 展示了在没有使用常量的情况下这个过程。

图 5-1 不使用常量计算三个产品的 20%增值税
尽管这个算法绝对正确,但问题是作者使用了 20%的增值税率(20/100)三次。如果这是一个实际的计算机程序,CPU 将被迫三次单独计算除法的结果(20/100)。
一般来说,除法和乘法是消耗 CPU 时间的操作,在可能的情况下应避免。
使用变量是一个更好的解决方案,如图 5-2 所示。这减少了除法操作的次数,也减少了打字错误的潜在可能性。

图 5-2 使用变量 vat 计算三个产品的 20%增值税
这次只计算一次除法(20/100),然后使用其结果来计算每个产品的增值税。但即使现在,算法(可能后来成为计算机程序)并不完美;vat 是一个变量,任何程序员都可能不小心在程序中将其值更改到以下内容。
理想解决方案是将变量 vat 更改为常量 VAT,如图 5–3 所示。
.
图 5–3 使用常量 VAT 计算三个产品的 20%增值税
注意,在流程图中声明常量时,使用等号(=)而不是左箭头。
这个最后的解决方案是许多原因的最佳选择。
►没有人,包括程序员,可以通过在程序中的任何位置不小心编写 VAT ← 0.60 之类的语句来仅通过偶然的方式更改常量增值税的值。
►减少打印错误的可能性。
►将算术运算的数量保持在尽可能低。
►如果有一天财政部长决定将增值税率从 20%提高到 22%,程序员只需更改一行代码!
5.3 存在多少种变量和常量类型?
大多数计算机语言中存在许多不同类型的变量和常量。这种多样性的原因是每个变量或常量可以存储不同类型的数据。大多数情况下,变量和常量存储以下类型的数据。
►整数:整数值是一个没有小数部分的正数或负数,例如 5、100、135、−25 和−5123。
►实数:实数是一个包含小数部分的正数或负数,例如 5.1、7.23、5.0、3.14 和−23.78976。实数也被称为浮点数。
►布尔值^([7]):布尔变量(或常量)只能存储两个值之一:True 或 False。
►字符:字符是一个字母数字值(一个字母、一个符号或一个数字),通常用单引号或双引号括起来,例如“a”,'c'或“@”。在计算机科学中,字符序列被称为字符串!!! 可能“字符串”这个词会让你联想到可穿戴的东西,但不幸的是,它不是。请保持你的脏脏的宝贵思维专注于计算机科学!字符串的例子有“Hello Zeus”、“I am 25 years old”或“Peter Loves Jane For Ever”。
在 Python 中,字符串可以用单引号或双引号括起来。
5.4 Python 中变量和常量的命名规则和约定
选择变量或常量的名称时必须遵循某些规则。
►变量或常量名称应仅由拉丁字符(英语大写或小写字母)、数字和下划线字符(_)组成。特别是对于常量,尽管允许使用小写字母,但建议只使用大写字母。这种约定有助于在视觉上区分常量和变量。变量名称的例子有 firstName、lastName1 和 age,而常量名称的例子有 VAT 和 COMPUTER_NAME。
►变量和常量名称是区分大小写的,这意味着大写和小写字符之间有明显的区别。例如,myVAR、myvar、MYVAR 和 MyVar 实际上是四个不同的名称。
►不允许有空格字符。如果变量或常量名称由多个单词组成,你可以在单词之间使用下划线字符(_)或在每个单词(除了第一个)的首字母大写(骆驼式命名约定)。例如,变量名称 student first name 是不正确的。相反,你可能使用 student_first_name,或者更好的是 studentFirstName。
►有效的变量或常量名称可以以字母或下划线开头。数字是被允许的,但不能用于名称的开头。例如,变量名称 1studentName 的写法是不正确的。相反,你可能使用类似 studentName1 或 student1Name 的名称。
►变量或常量名称通常选择的方式可以描述它所包含数据的含义和作用。例如,一个存储温度值的变量可能被命名为 temperature、temp 或甚至 t。
►不要使用 Python 的任何保留字作为变量或常量名称。例如,名称 While 不能作为有效的变量或常量名称,因为在 Python 中它是一个保留字。
不幸的是,Python 不支持常量,就像某些其他计算机语言(如 C#或 C++)所做的那样。你可以使用变量代替,但要注意不要在程序中使用这个变量时意外地改变其初始值。
"骆驼式命名约定"是计算机编程中命名标识符(变量、子程序、类等)的一种风格。之所以称为"骆驼式",是因为名称中间的大写字母类似于骆驼的驼峰。骆驼式命名主要有两种变体:a) 小驼峰式(或骆驼式),其中标识符的第一个字母以小写字母开头,每个后续单词的第一个字母也以大写字母开头;b) 大驼峰式(或帕斯卡式),与大驼峰式类似,但标识符的第一个字母也以大写字母开头。
小驼峰式命名约定通常用于命名变量和子程序,而大驼峰式用于命名类。你将在相应的第七部分 Part VII 和第八部分 Part VIII 中了解更多关于子程序和类的内容。
5.5 “声明变量”这个短语是什么意思?
声明是为主存储器(RAM)中存储变量内容预留部分的过程。在许多高级计算机语言中,程序员必须在变量可以使用之前编写一个特定的语句来预留该部分在 RAM 中的空间。在大多数情况下,他们甚至需要指定变量类型,以便编译器或解释器知道需要预留多少空间。
下面是一些示例,展示了如何在不同的高级计算机语言中声明一个变量。
| 声明语句 | 高级计算机语言 |
|---|---|
| Dim sum As Integer | Visual Basic |
| int sum; | C#, C, C++, Java, 以及更多 |
| sum: Integer; | Pascal, Delphi |
| var sum; | Javascript |
5.6 如何在 Python 中声明变量
在 Python 中,不需要像在 C# 或 C++ 中那样声明变量。变量是在首次使用时声明的。
下面是一些示例,展示了在 Python 中声明一些变量(用作常量)。它们的名称遵循小驼峰命名法以及 第 5.4 节 中提到的所有变量命名规则。
number1 = 0
found = False
firstName = "Hera"
在 Python 中,使用等号(=)给变量赋值。这个操作与流程图中的左箭头等效。
在 Python 中,使用双引号(" ")给字符串类型的变量赋值。
5.7 如何在 Python 中声明常量
你必须接受它!在 Python 中无法声明常量,没有这样的选项!然而,你仍然可以使用变量(最好是使用大写字母)代替,并且在使用这些变量时要注意不要改变它们的初始值。
以下示例在 Python 中声明了一些变量(用作常量)。它们遵循 第 5.4 节 中提到的所有常量命名规则和约定。
VAT = 0.22
NUMBER_OF_PLAYERS = 25
FAVORITE_SONG = "We are the world"
FAVORITE_CHARACTER = "w"
在计算机编程中,一旦定义了常量,在程序运行期间其值不能改变。不幸的是,Python 并不是这样。由于你使用变量代替常量,你必须小心不要在程序中意外地改变它们的初始值。
5.8 复习问题:真/假
对于以下每个陈述,选择真或假。
1)变量是计算机二级存储设备中的一个位置。
2)在流程图中,对于赋值操作符,你可以使用左箭头或右箭头。
3)在程序执行过程中,变量的内容可以改变。
4)在 Python 之外的语言中,常量的内容在程序执行过程中可以改变。
5)值 10.0 是一个整数。
6)布尔变量只能存储两个值中的一个。
7)双引号括起来的值“10.0”是一个实数。
8)在计算机科学中,字符串是你能佩戴的东西!
9)变量的名称可以包含数字。
10)变量可以在程序执行时更改其名称。
11)变量的名称不能是数字。
12)常量的名称必须始终是描述性的。
13)student name 不是一个有效的变量名。
14)STUDENT_NAME 是一个有效的常量名。
15)在 Python 中,变量的名称可以包含大写和小写字母。
16)在 Python 中,不需要声明变量。
17)在 Python 程序中,你必须始终至少使用一个变量。
5.9 复习问题:多项选择题
为以下每个陈述选择正确的答案。
1)变量是一个存储
a)一个硬盘。
b)一个 DVD 光盘。
c)一个 U 盘。
d)所有这些
e)以上都不是
2)变量可以持有
a)一次一个值。
b)一次多个值。
c)所有这些
d)以上都不是
3)通常,在程序中使用常量
a)帮助程序员完全避免拼写错误。
b)帮助程序员避免使用除法和乘法。
c)所有这些
d)以上都不是
4)以下哪一个是整数?
a)5.0
b)−5
c)“5”
d)以上都不是整数。
5)布尔变量可以持有值
a)一个。
b)“True”。
c)True。
d)以上都不是
6)在 Python 中,字符串可以是
a)用单引号括起来。
b)用双引号括起来。
c)两者都是
7)以下哪项不是有效的 Python 变量?
a)city_name
b)cityName
c)cityname
d)city-name
5.10 复习练习
完成以下练习。
1)将第一列中的每个元素与第二列中的一个元素匹配。
| 值 | 数据类型 |
|---|---|
| 1. “True” | a. 布尔 |
| 2. 123 | b. 实数 |
| 3. 假 | c. 字符串 |
| 4. 10.0 | d. 整数 |
2)将第一列中的每个元素与第二列中的一个元素匹配。
| 值 | 数据类型 |
|---|---|
| 1. 人的名字 | a. 布尔 |
| 2. 人的年龄 | b. 实数 |
| 3. 5.0/2.0 的除法结果 | c. 整数 |
| 4. 是黑色还是白色? | d. 字符串 |
3)完成以下表格
| 值 | 数据类型 | 声明和初始化 |
|---|---|---|
| 我朋友的名字 | 字符串 | name = "Mark" |
| 我的地址 | address = "254 Lookout Rd. Wilson, NY 27893" | |
| 平均每日气温 | ||
| 一个电话号码 | phoneNumber = "1‑891‑764‑2410" | |
| 我的社保号码(SSN) | ||
| 汽车的速度 | ||
| 家庭中的孩子数量 |
第六章
处理输入和输出
6.1 如何将消息和结果输出到用户的屏幕?
流程图使用斜平行四边形和保留词“Write”来显示消息或最终结果到用户的屏幕上。

其中 arg1、arg2 和 arg3 可以是变量、表达式、常量值或双引号括起来的字母数字值。
你刚才看到的斜平行四边形等价于以下流程图片段。

在 Python 中,你可以通过使用 print 语句来实现相同的结果。其一般形式是
print(arg1, arg2, arg3, … [, sep = " "] [, end = "\n"])
其中
►arg1、arg2、arg3、… 是要打印的参数(值)。它们可以是变量、表达式、常量值或单引号或双引号括起来的字符串。
►sep 是插入在参数之间的字符串。它是可选的,默认值是一个空格字符。
►end 是附加在最后一个参数之后的字符串。它是可选的,默认值是一个“换行符”。
以下代码片段:
a = 5
b = 6
c = a + b
print("5 和 6 的和是", c)
显示图 6–1 中所示的消息。

图 6–1 屏幕上显示的字符串和整数
在 Python 中,如果你想将字符串显示在屏幕上,该字符串必须用单引号或双引号括起来。
注意屏幕上在第一个字符串和变量 c 的值(就在“是”这个词之后)之间自动插入的空格。
数学表达式的结果也可以直接在 print 语句中计算。以下代码片段显示的正是图 6–1 中的相同消息。
a = 5
b = 6
print("5 和 6 的和是", a + b)
6.2 如何修改 print 语句的默认行为?
如前所述,Python 自动在参数之间输出一个空格。以下示例
print("Morning", "Evening", "Night")
显示

图 6–2 输出结果显示参数之间有一个空格
还要注意,以下三个语句无论逗号(,)分隔符之后有多少空格字符,都会产生相同的输出结果:
print("Morning","Evening","Night")
print("Morning", "Evening", "Night")
print("Morning", "Evening", "Night")
如图 6–3 所示。

图 6–3 输出结果始终在参数之间显示一个空格
如果你想自定义分隔符字符,你需要像这里一样为参数 sep 使用一个值:
print("Morning", "Evening", "Night", sep = "#")
并且输出结果现在如图 6–4 所示。

图 6–4 带有自定义分隔符的输出结果
现在,仔细看看以下示例。
a = "Ares"
print("Hello", a)
print("Halo", a)
print("Salut", a)
Python 中的 print 语句在最后一个参数(变量 a)之后自动添加一个“换行符”;因此,这三个消息像图 6–5 所示的那样一个接一个地显示。
.
图 6–5 输出结果显示在三条线上
你可以自定义 end 参数的值,如下所示:
a = "Ares"
print("Hello", a, end = " - ")
print("Halo", a, end = " - ")
print("Salut", a)
输出结果现在如图 6–6 所示。

图 6–6 输出结果显示在一行上
一个有趣的字符序列是 \n,它可以用来创建一个自定义的“换行符”。你可以像下面这样输出一个“换行符”:
a = "Ares"
print("Hello", a, "\nHalo", a, "\nSalut", a)
并且输出结果如图 6–7 所示。

图 6–7 输出结果显示在三条线上
另一个有用的字符序列是 \t,它可以用来创建一个“制表位”。制表符(\t)对于对齐输出很有用。
print("John\tGeorge")
print("Sofia\tMary")
输出结果如图 6–8 所示。
.
图 6–8 输出结果显示制表符
当然,同样的结果可以用一个单独的语句实现。
print("John\tGeorge\nSofia\tMary")
6.3 如何提示用户输入数据?
你还记得创建算法或计算机程序涉及的主要三个阶段吗?第一个阶段是“数据输入”阶段,在这个阶段,计算机允许用户输入数据,如数字、他们的名字、他们的地址或他们的出生年份。
流程图使用斜平行四边形和保留词“Read”来让用户输入他们的数据。

其中 var_name1、var_name2 和 var_name3 必须是变量。
你刚才看到的斜平行四边形等价于以下流程图片段。
.
当执行 Read 语句时,执行流程会中断,直到用户输入所有数据。当数据输入完成后,执行流程继续到下一个语句。通常数据是从键盘输入的。
在 Python 中,数据输入是通过以下语句之一完成的:
从键盘读取字符串
var_name_str = input([prompt])
从键盘读取整数
var_name_int = int(input([prompt]))
从键盘读取实际数据
var_name_float = float(input([prompt]))
其中
►prompt 是要显示的提示信息。它可以是变量或用单引号或双引号括起来的字符串。prompt 参数是可选的。
►var_name_str 可以是任何类型为字符串的变量。
►var_name_int 可以是任何类型为整数的变量。
►var_name_float 可以是任何类型为 float(实数)的变量。
函数 int() 和 float() 将在本书的后面讨论。
让我们通过以下示例来研究 input() 语句。
name = input("What is your name? ")
print("你好", name)
当此示例中的 input()语句执行时,会显示消息“你叫什么名字?”(不带双引号),执行流程会停止,等待用户输入他们的名字。print()语句尚未执行!只要用户没有输入任何内容,计算机就会等待,如图 6–9 所示。
.
Figure 6–9 当 input()语句执行时,计算机等待数据输入。
当用户最终输入他们的名字并按下“Enter
”键时,执行流程随后继续到下一个 print()语句,如图 6–10 所示。
.
Figure 6–10 当用户按下“Enter
”键时,执行流程继续。
以下代码片段提示用户输入一个字符串和一个浮点数,即包含小数部分的数字。
productName = input("输入产品名称: ")
productPrice = float(input("输入产品价格: "))
以下程序提示用户输入一个字符串和一个整数,即不带小数部分的数字,然后显示一条消息。
name = input("你叫什么名字? ")
age = int(input("你多大了? "))
print("哇,你已经", age, "岁了,", name, "!")
在 Python 中(尽管不常用),你可以只用一行代码读取两个或更多值,如下所示。
name, age = input("你叫什么名字? "), int(input("你多大了? "))
print("哇,你已经", age, "岁了,", name, "!")
然而,相应的流程图片段必须看起来像这样

在 Python 中,建议使用 input()语句来显示提示信息。以下示例是正确的,但并不常用。
print("你叫什么名字? ", end = "")
name = input()
print("你多大了? ", end = "")
age = int(input())
print("哇,你已经", age, "岁了,", name, "!")
在这本书中,“提示”和“允许”这两个词之间有一点区别。当一个练习说“编写一个 Python 程序,提示用户输入...”这意味着你必须包含一个提示信息。然而,当一个练习说“编写一个 Python 程序,允许用户输入...”这意味着实际上并不要求包含提示信息;也就是说,包含它是没有错的,但你不必这么做!以下示例允许用户输入他们的名字和年龄(但不提示他们)。
name = input()
age = int(input())
当程序执行时,这里发生的情况是计算机显示一个文本光标而没有任何提示信息,等待用户输入两个值——一个用于名字,一个用于年龄。然而,用户必须是一个先知,猜测要输入什么!他们必须先输入名字然后输入年龄,还是相反?所以,显然需要一个提示信息,因为它会使你的程序更加用户友好。
6.4 复习问题:判断题
对以下每个陈述选择正确或错误。
1)在 Python 中,单词 print 是一个保留字。
2)可以使用 print()语句显示消息或变量的内容。
3)当执行 input()语句时,执行流程会中断,直到用户输入一个值。
4)一个单独的 input()语句可以用来输入多个数据值。
5)在数据输入之前,必须始终显示提示信息。
6.5 复习问题:多项选择题
选择以下每个陈述的正确答案。
1)语句 print(hello)显示
a)单词“hello”(不使用双引号)。
b)单词“hello”(包括双引号)。
c)变量 hello 的内容。
d)以上都不是
2)语句 print("HELLO")显示
a)单词“HELLO”(不使用双引号)。
b)单词“HELLO”(包括双引号)。
c)变量 HELLO 的内容。
d)以上都不是
3)语句 print("Hello\nHermes")显示
a)输入消息“Hello Hermes”(不使用双引号)。
b)在第一行输入单词“Hello”,在下一行输入单词“Hermes”(不使用双引号)。
c)消息“HelloHermes”(不使用双引号)。
d)消息“Hello\nHermes”(不使用双引号)。
e)以上都不是
4)语句 data1data2 = input()
a)允许用户输入一个值并将其分配给变量 data1. 变量 data2 保持为空。
b)允许用户输入一个值并将其分配给变量 data1data2。
c)允许用户输入两个值并将它们分配给变量 data1 和 data2。
d)以上都不是
第七章
运算符
7.1 值赋值运算符
Python 中最常用的运算符是值赋值运算符(=)。例如,以下 Python 语句将值 5 赋给变量 x。
x = 5
正如你在第五章中读到的,这相当于流程图中使用的左箭头。
.
可能流程图中使用的左箭头比(=)符号更方便、更清晰,因为它直观地说明了右侧表达式或结果的值被赋值给左侧的变量。
重要的是要注意,(=)符号在数学中并不等同于 Python 中的使用。在数学中,表达式 x = 5 读作“x 等于 5”。然而,在 Python 中,表达式 x = 5 读作“将值 5 赋给 x”或“将 x 设置为 5”。它们看起来一样,但作用不同!
例如,在数学中,以下两行是等价的。第一行可以读作“x 等于 y 和 z 的和”,第二行可以读作“y 和 z 的和等于 x”。
x = y + z
y + z = x
另一方面,在 Python 中,这两个语句绝对不等价。第一个语句是一个有效的 Python 语句,表示“将 y 和 z 的和赋值给 x”。然而,第二个语句是无效的,因为它试图将 x 的值赋给 y + z,这在 Python 中是不允许的!
在 Python 中,(=)符号左侧的变量代表主内存(RAM)中的一个区域,可以存储值。
在(=)符号的左侧只能存在变量,而在右侧则可以是数字、变量、字符串,甚至是复杂的数学表达式。
在表 7-1 中,你可以找到一些值赋值的例子。
| 示例 | 描述 |
|---|---|
| a = 9 | 将值 9 赋给变量 a。 |
| b = c | 将变量 c 的内容赋值给变量 b。 |
| d = "Hello Zeus" | 将字符串 Hello Zeus 赋值给变量 d。 |
| d = a + b | 计算变量 a 和 b 的内容之和,并将结果赋值给变量 d。 |
| b = x + 1 | 计算变量 x 的内容和 1 的和,并将结果赋值给变量 b。请注意,变量 x 的内容没有被改变。 |
| x = x + 1 | 计算变量 x 的内容和 1 的和,并将结果重新赋值给变量 x。换句话说,将变量 x 增加 1。 |
表 7-1 值赋值例子
对于最后一个例子感到困惑吗?你现在在想你的数学老师吗?如果你在黑板上写下 x = x + 1,他们会怎么反应?你能想象一个等于自己加一的数字吗?这个语句暗示 5 等于 6,10 等于 11,这当然是错误的!
显然,在计算机科学中事情是不同的。语句 x = x + 1 是完全有效的!它指示 CPU 从主存(RAM)中检索变量 x 的值,将 1 加到该值上,并将结果重新赋值给变量 x。变量 x 的旧值被新值所取代。
仍然不明白?让我们看看 CPU 和主存(RAM)是如何相互协作以执行语句 x = x + 1 的。
假设内存中有一个名为 x 的区域,它包含数字 13。

当一个程序指示 CPU 执行以下语句时:
x = x + 1
以下程序被执行:
►数字 13 从 RAM 中名为 x 的区域传输到 CPU;
►CPU 计算 13 和 1 的和;并且
►结果,14,从 CPU 传输到 RAM 的 x 区域,替换现有的数字 13。
执行后,RAM 看起来是这样的。
.
现在你已经理解了一切,让我们深入探讨一些最终细节。在 Python 中,你可以用一条语句将单个值赋给多个变量。以下语句:
a = b = c = 4
将 4 的值赋给所有三个变量 a、b 和 c。
在 Python 中,你还可以用一条语句将多个值赋给多个变量。这被称为同时赋值。以下语句将 2 的值赋给变量 a,10 的值赋给变量 b,3 的值赋给变量 c。
a, b, c = 2, 10, 3
7.2 算术运算符
就像每种高级编程语言一样,Python 支持几乎所有类型的算术运算符,如下表所示。
| 算术运算符 | 描述 |
|---|---|
| + | 加法 |
| − | 减法 |
| * | 乘法 |
| / | 除法 |
| // | 整数除法后的商 |
| % | 整数除法后的余数(模数) |
| ** | 幂运算 |
前两个运算符很简单,不需要进一步解释。
如果你需要乘以两个数字或两个变量的内容,你必须使用星号(*)符号。例如,如果你想乘以 2 乘以 y,你必须写 2 * y。
在数学中,可以省略乘法运算符并写 3x,表示“3 乘以 x”。然而,在 Python 中,你必须始终在任何存在乘法操作的地方使用星号(*)。这是新手程序员在 Python 中编写数学表达式时最常见的错误之一。
进行除法时,必须使用斜杠(/)符号。例如,如果你想将 10 除以 2,你必须写 10 / 2。
整数除法运算符(//)返回整数除法的商,这意味着
a = 13 // 3
将 4 的值赋给变量 a。
(//)运算符也可以用于浮点数。例如,操作
b = 14.4 // 3
将 4 的值赋给变量 b。
相应地,取模运算符(%)返回整数除法的余数,这意味着
c = 13 % 3
将值 1 赋给变量 c。
取模运算符(%)也可以与浮点数一起使用,但结果是实数(float)。例如,操作
d = 14.4 % 3
将 2.4(而不是您可能错误地期望的 2)的值赋给变量 d。
请记住,流程图是用来表示算法的一种松散方法。尽管在流程图中允许使用整数商(//)和整数余数(%)运算符,但本书使用的是普遍接受的 DIV 和 MOD 运算符!例如,Python 语句 x = 13 // 3 和 y = 13 % 3 在流程图中表示为
.
指数运算符(**)将运算符左侧的数提升到运算符右侧的数的幂。例如,操作
f = 2 ** 3
计算 2 的 3 次幂(2³)并将值 8 赋给变量 f。
指数运算符(**)具有双重作用。除了用于计算一个值被另一个值提升的幂之外,它还用于使用已知的数学公式
计算一个数的任何根。例如,您可以编写 y = x ** (1 / 2)来计算 x 的平方根或 y = x ** (1 / 3)来计算 x 的立方根。
在数学中,如您可能已经知道的,您可以使用括号(圆括号)以及花括号(花括号)和方括号,如下面的表达式所示。
.
然而,在 Python 中,没有括号和方括号这样的东西。只有括号;因此,必须使用括号而不是花括号或方括号来编写相同的表达式。
y = 5 / 2 * (3 + 2 * (4 + 7 * (6 − 4 / 3)))
7.3 算术运算符的优先级是什么?
算术运算符遵循与数学中相同的优先级规则,这些规则是:首先执行指数运算,然后执行乘法和除法,最后执行加法和减法。
| 高优先级 
优先级较低 | 算术运算符 |
| ** |
|---|
| *, /, //, % |
| +, − |
当乘法和除法存在于同一个表达式中,并且由于它们具有相同的优先级,它们将按从左到右的顺序执行(与阅读方式相同),这意味着表达式
y = 6 / 3 * 2
等价于
,并将值 4 赋给变量 y,(除法在乘法之前执行)。
如果您希望乘法在除法之前执行,则可以使用括号来改变优先级。这意味着
y = 6 / (3 * 2)
等价于
,并将值 1 赋给变量 y(乘法在除法之前执行)。
请记住,在 Python 中无法以
或
的形式写入分数。别想了!Visual Studio Code 或任何 IDE 中都没有方程式编辑器。所有分数都必须写在一行上。例如,
必须写成 6 / 3,而
必须写成 (4 * x + 5) / 6。
运算顺序可以总结如下:
1)首先执行括号内的任何运算。
2)接下来执行任何指数运算。
3)然后,从左到右执行任何乘法和除法运算。
4)最后,任何加法和减法运算都是从左到右执行的。
因此,在下一个示例中
y = 12 + (20 + 3) - 2 ** 3 / 4 * 3
操作的执行顺序如下:

7.4 复合赋值运算符
Python 提供了一组称为复合赋值运算符的特殊运算符,这可以帮助你更快地编写代码。这些运算符在下面的表格中进行了全面详细的说明。每个运算符都提供了一个示例,而“等价于”列显示了不使用复合赋值运算符的相应语句。
| 运算符 | 描述 | 示例 | 等价于 |
|---|---|---|---|
| += | 加法赋值 | a += b | a = a + b |
| −= | 减法赋值 | a ‑= b | a = a ‑ b |
| *= | 乘法赋值 | a *= b | a = a * b |
| /= | 除法赋值 | a /= b | a = a / b |
| //= | 整数除法赋值 | a //= b | a = a // b |
| %= | 取模赋值 | a %= b | a = a % b |
| **= | 幂赋值 | a **= b | a = a ** b |
请注意,在流程图中,本书只使用了“等价于”列中显示的普遍接受的运算符。例如,Python 语句 a += b 在流程图中表示为
.
练习 7.4-1 哪些 Python 语句在语法上是正确的?
以下哪个 Python 赋值语句在语法上是正确的?
i) a = −10
ii)10 = b
iii)aB = aB + 1
iv)a = "COWS"
v)a = COWS
vi)a + b = 40
vii) a = 3 b
viii)a = "True"
ix)a = True
x)a //= 2
xi)a += 1
xii)a =* 2
解答
i) 正确。它将整数值 −10 赋值给变量 a。
ii) 错误。在值赋值运算符的左侧只能存在变量。
iii) 正确。它将变量 aB 的值增加一。
iv) 正确。它将字符串“COWS”(不带双引号)赋值给变量 a。
v) 正确。它将变量 COWS 的内容赋值给变量 a。
vi) 错误。在值赋值运算符的左侧只能存在变量(不是表达式)。
vii) 错误。它应该写成 a = 3 * b。
viii) 正确。它将字符串“True”(不带双引号)赋值给变量 a。
ix) 正确。它将值 True 赋值给变量 a。
x) 正确。这相当于 a = a // 2。
xi) 正确。这相当于 a = a + 1。
xii)错误。它应该写成 a *= 2(等价于 a = a * 2)。
练习 7.4-2 查找变量类型
以下每个变量的类型是什么?
i)a = 15
ii)width = "10 meters"
iii)b = "15"
iv)temp = 13.5
v)b = True
vi)b = "True"
解答
i)值 15 属于整数的集合,因此变量 a 是整数。
ii)值“10 meters”是文本,因此宽度变量是字符串。
iii)值“15”是一个文本,因此变量 b 是一个字符串。
iv)值 13.5 属于实数集合,因此变量 temp 是实数(浮点数)。
v)值 True 是布尔值,因此变量 b 是布尔值。
vi)值“True”是文本,因此变量 b 是字符串。
7.5 字符串运算符
将两个独立的字符串连接成一个字符串称为连接。你可以使用以下表格中显示的两个运算符来连接(连接)字符串。
| 运算符 | 描述 | 示例 | 等价于 |
|---|---|---|---|
| + | 连接 | a = "Hi" + " there" | |
| += | 连接赋值 | a += "Hello" | a = a + "Hello" |
以下示例显示“What's up, dude?”
a = "What's "
b = "up, "
c = a + b
c += "dude?"
print(c)
最后但同样重要的是,还有两个可以用于字符串重复的字符串运算符,如下表所示。
| 运算符 | 描述 | 示例 | 等价于 |
|---|---|---|---|
| * | 重复 | a = "Hi" * 3 | a = "HiHiHi" |
| *= | 重复赋值 | a *= 5 | a = a * 5 |
以下示例首先显示“catcat”,然后显示“catcatcatcat”。
a = "cat" * 2
print(a) #它显示:catcat
a *= 2
print(a) #它显示:catcatcatcat
练习 7.5-1 连接名字
编写一个 Python 程序,提示用户输入他们的名字(分配给两个不同的变量)。然后它们将它们连接成一个字符串(连接)并在用户的屏幕上显示。
解答
Python 程序如下所示。
firstName = input("Enter first name: ")
lastName = input("Enter last name: ")
fullName = firstName + " " + lastName
print(fullName)
注意在名字的首尾之间添加了额外的空格字符。
7.6 复习问题:正确/错误
对以下每个陈述选择正确或错误。
1)语句 x = 5 可以读作“变量 x 等于 5”。
2)值赋值运算符将表达式的结果赋给变量。
3)只能通过使用 input()语句将字符串赋值给变量。
4)语句 5 = y 将值 5 赋给变量 y。
5)在值赋值运算符的右边必须始终存在算术运算符。
6)在 Python 中,值赋值运算符的右边只能存在变量。
7)在值赋值运算符的两边不能使用相同的变量。
8)语句 a = a + 1 减少了变量 a 的值。
9)语句 a = a + (−1) 减少了变量 a 的值。
10)在 Python 中,单词 DIV 是一个保留字。
11)语句 x = 0 % 5 将值 5 赋给变量 x。
12)操作 5 % 0 是不可能的。
13)加法和减法在算术运算符中具有更高的优先级。
14)当表达式中存在除法和乘法运算符时,乘法运算先于除法运算执行。
15)语句 2 ** 3 返回结果 6。
16)语句 2 ** 3 返回结果 9。
17)表达式 8 / 4 * 2 等于 1。
18)表达式 4 + 6 / 6 + 4 等于 9。
19)表达式 a + b + c / 3.0 计算三个数的平均值。
20)语句 a += 1 等价于 a = a + 1
21)语句 a = "True"将布尔值赋给变量 a。
22)语句 a = 2·a 使变量 a 的内容加倍。
23)语句 a += 2 和 a = a − (−2)不等价。
24)语句 a −= a + 1 总是将变量 a 的值赋为-1。
25)语句 a = "George" + " Malkovich"将值“GeorgeMalkovich”(不带双引号)赋给变量 a。
26)以下 Python 程序满足确定性的性质。
a = float(input())
b = float(input())
x = a / (b - 7)
print(x)
7.7 复习题:多项选择题
为以下每个陈述选择正确的答案。
1)以下哪个 Python 赋值语句将 10.0 的值赋给变量 a?
a)10.0 = a
b)a ← 10.0
c)a = 100.0 / 10.0
d)以上皆非
2)以下语句可以读作
a)将变量 a 的内容赋给变量 b。
b)变量 b 等于变量 a。
c)将变量 b 的内容赋给变量 a。
d)以上皆非
3)以下哪个计算变量 a 的平方?
a)y = a * a
b)y = a ** 2
c)y = a * a / a * a
d)全部正确
4)表达式 0 % 10 + 2 等于
a)7.
b)2.
c)12.
d)以上皆非
5)以下哪个 Python 语句是语法正确的?
a)a = 4 * 2y − 8 / (4 * q)
b)a = 4 * 2 * y − 8 /> 4 * q)
c)a = 4 * 2 * y − 8 /> 4 * q)
d)以上皆非
6)以下哪个 Python 语句是语法正确的?
a)a ** 5 = b
b)b = a ** 5
c)a =** 5
d)以上皆非
7)以下哪个 Python 语句将值“George Malkovich”(不带双引号)赋给变量 a?
a)a = "George" + " " + "Malkovich"
b)a = "George" + " Malkovich"
c)a = "George " + "Malkovich"
d)以上皆非
8)以下代码片段
x = 2
x += 1
不满足
a)确定性。
b)确定性。
c)有效性。
d)以上皆非
9)以下代码片段
a = float(input())
x = 1 / a
不满足
a)确定性。
b)输入。
c)确定性。
d)以上皆非
7.8 复习题
完成以下练习。
1)以下哪个 Python 赋值语句是语法正确的?
i)a ← a + 1
ii)a += b
iii)a b = a b + 1
iv)a = a + 1
v)a = hello
vi)a = 40"
vii)a = b · 5
viii)a =+ "True"
ix)fdadstwsdgfgw = 1
x)a = a ** 5
2)以下每个变量的类型是什么?
i)a = "False"
ii)w = False
iii)b = "15 meters"
iv)weight = "40"
v)b = 13.0
vi)b = 13
3)将第一列的每个元素与第二列的每个元素匹配。
| 操作 | 结果 |
|---|---|
| i) 1 / 2.0 | a) 100 |
| ii) 1.0 / 2 * 2 | b) 0.25 |
| iii) 0 % 10 * 10 | c) 0 |
| iv) 10 % 2 + 7 | d) 0.5 |
| e) 7 | |
| f) 1.0 |
4)执行以下代码片段后,屏幕上显示什么?
i)
a = 5
b = a * a + 1
打印(b)
b += 1
ii)
a = 9
b = a / 3 * a
打印(b + 1)
5)执行以下代码片段后,屏幕上显示什么?
i)
a = 5
a += a - 5
打印(a)
ii)
a = 5
a = a + 1
打印(a)
6)以下每个操作的运算结果是什么?
i)21 % 5
ii)10 % 2
iii)11 % 2
iv)10 % 6 % 3
v)0 % 3
vi)100 / 10 % 3
7)执行以下代码片段后,屏幕上显示什么?
i)
a = 5
b = 2
c = a % (b + 1)
d = (b + 1) % (a + b)
打印(c, "*", d)
ii)
a = 4
b = 8
a += 1
c = a * b / 10 % b
打印(c)
8)计算以下情况下 a % b 的结果。
i)a = 20, b = 3
ii)a = 15, b = 3
iii)a = 22, b = 3
iv)a = 0, b = 3
v)a = 3, b = 1
vi)a = 2, b = 2
9)计算以下表达式的结果
b * (a % b) + a / b
对以下每种情况。
i)a = 10, b = 5
ii)a = 10, b = 2
10)执行以下代码片段后,屏幕上显示什么?
a = "My name is"
a += " "
a = a + "George Malkovich"
打印(a)
11)在以下代码片段中填入空缺,以便它们都显示值为 5。
i)
a = 2
a = a - ……
打印(a)
ii)
a = 4
b = a * 0.5
b += a
a = b - ……
打印(a)
12)执行以下代码片段后,屏幕上显示什么?
city = "California"
California = city
打印(city, California, "California")
第八章
跟踪表
8.1 什么是跟踪表?
跟踪表是一种用于测试算法或计算机程序在执行过程中出现的逻辑错误的技巧。
跟踪表模拟执行流程。语句按步骤执行,变量的值在执行赋值语句时发生变化。
跟踪表在教育方面很有用。它们通常由新手程序员使用,以帮助他们可视化特定算法或程序的工作方式,并帮助他们检测逻辑错误。
一个典型的跟踪表如下所示。
| 步骤 | 语句 | 备注 | 变量 1 | 变量 2 | 变量 3 |
|---|---|---|---|---|---|
| 1 | |||||
| 2 | |||||
| … |
让我们看看跟踪表的实际应用!对于以下 Python 程序,创建了一个跟踪表来确定每一步的变量值。
x = 10
y = 15
z = x * y
z += 1
打印 z
下面显示了此程序的跟踪表。备注是可选的,但它们有助于读者更好地理解实际发生的情况。
| 步骤 | 语句 | 备注 | x | y | z |
|---|---|---|---|---|---|
| 1 | x = 10 | 将值 10 赋值给变量 x。 | 10 | ? | ? |
| 2 | y = 15 | 将值 15 赋值给变量 y。 | 10 | 15 | ? |
| 3 | z = x * y | 乘积 x * y 的结果赋值给 z。 | 10 | 15 | 150 |
| 4 | z += 1 | 变量 z 增加了 1。 | 10 | 15 | 151 |
| 5 | print(z) | 它显示:151 |
练习 8.1-1 创建跟踪表
创建一个跟踪表,以确定 Python 程序两次执行中每一步的变量值。
两次执行的输入值分别为:(i) 0.3,和 (ii) 4.5。
b = float(input())
c = 3
c = c * b
a = 10 * c
a = a % 10
打印 a
解答
i)对于输入值 0.3,跟踪表如下所示。
| 步骤 | 语句 | 备注 | a | b | c |
|---|---|---|---|---|---|
| 1 | b = float(input()) | 用户输入值 0.3 | ? | 0.3 | ? |
| 2 | c = 3 | ? | 0.3 | 3 | |
| 3 | c = c * b | ? | 0.3 | 0.9 | |
| 4 | a = 10 * c | 9.0 | 0.3 | 0.9 | |
| 5 | a = a % 10 | 9.0 | 0.3 | 0.9 | |
| 6 | print(a) | 它显示:9.0 |
ii)对于输入值 4.5,跟踪表如下所示。
| 步骤 | 语句 | 备注 | a | b | c |
|---|---|---|---|---|---|
| 1 | b = float(input()) | 用户输入值 4.5 | ? | 4.5 | ? |
| 2 | c = 3 | ? | 4.5 | 3 | |
| 3 | c = c * b | ? | 4.5 | 13.5 | |
| 4 | a = 10 * c | 135.0 | 4.5 | 13.5 | |
| 5 | a = a % 10 | 5.0 | 4.5 | 13.5 | |
| 6 | print(a) | 它显示:5.0 |
练习 8.1-2 创建跟踪表
当执行以下程序时,显示什么结果?
Ugly = "Beautiful"
Beautiful = "Ugly"
Handsome = Ugly
打印("Beautiful")
打印 Ugly
打印 Handsome
解答
让我们创建一个跟踪表来找到输出结果。
| 步骤 | 语句 | 备注 | Ugly | Beautiful | Handsome |
|---|---|---|---|---|---|
| 1 | Ugly = "Beautiful" | 将字符串 "Beautiful" 赋值给变量 Ugly。 | Beautiful | ? | ? |
| 2 | Beautiful = "Ugly" | The string “Ugly” is assigned to the variable Beautiful. | Beautiful | Ugly | ? |
| 3 | Handsome = Ugly | The value of variable Ugly is assigned to the variable Handsome. | Beautiful | Ugly | Beautiful |
| 4 | print("Beautiful") | It displays: Beautiful | |||
| 5 | print(Ugly) | It displays: Beautiful | |||
| 6 | print(Handsome) | It displays: Beautiful |
练习 8.1-3 变量值交换
编写一个 Python 程序,让用户输入两个值,分别存储在变量 a 和 b 中。程序结束时,两个变量必须交换它们的值。例如,如果变量 a 和 b 分别包含值 5 和 7,在交换它们的值后,变量 a 必须包含值 7,变量 b 必须包含值 5!
解决方案
以下程序,尽管看起来正确,但实际上并没有交换变量 a 和 b 的值!
a = int(input())
b = int(input())
a = b
b = a
print(a)
print(b)
让我们看看为什么!假设用户输入两个值,5 和 7。跟踪表如下所示。
| Step | Statement | Notes | a | b |
|---|---|---|---|---|
| 1 | a = int(input()) | User enters the value 5 | 5 | ? |
| 2 | b = int(input()) | User enters the value 7 | 5 | 7 |
| 3 | a = b | The value of variable b is assigned to variable a. Value 5 is lost! | 7 | 7 |
| 4 | b = a | The value of variable a is assigned to variable b | 7 | 7 |
| 5 | print(a) | It displays: 7 | ||
| 6 | print(b) | It displays: 7 |
哎呀!5 这个值去哪了?
解决方案并不像最初想象的那么明显!那么,你到底是如何交换值的呢?
考虑两个杯子:一杯橙汁(称为杯子 A),一杯柠檬汁(称为杯子 B)。如果你想交换它们的内容,你只需找到并使用一个额外的空杯子(称为杯子 C)。

必须遵循以下步骤:
- 将杯子 A(橙汁)的内容倒入杯子 C。
.
- 将杯子 B(柠檬汁)的内容倒入杯子 A。
.
- 将杯子 C(橙汁)的内容倒入杯子 B。
.
交换完成成功!
你可以遵循相同的步骤在 Python 中交换两个变量的内容。
a = int(input())
b = int(input())
c = a # 将杯子 A(橙汁)的内容倒入杯子 C
a = b # 将杯子 B(柠檬汁)的内容倒入杯子 A
b = c # 将杯子 C(橙汁)的内容倒入杯子 B
print(a)
print(b)
The text after a hash character is considered a comment and is never executed.
最后但同样重要的是,在 Python 中,你也可以像这样交换两个变量的内容:
a = int(input())
b = int(input())
a, b = b, a
print(a)
print(b)
练习 8.1-4 变量值交换 – 另一种方法
编写一个 Python 程序,让用户输入两个整数值,存储在变量 a 和 b 中。最后,两个变量必须交换它们的值。然后,使用输入值 5 和 7 的跟踪表来确认代码的正确性。
解答
Since the variables contain numeric values, you can use the following Python program (as an alternative approach).
a = int(input())
b = int(input())
a = a + b
b = a - b
a = a - b
print(a, b)
让我们现在使用输入值 5 和 7 的跟踪表来确认变量 a 和 b 正确地交换了它们的内容。
| 步骤 | 语句 | 备注 | a | b |
|---|---|---|---|---|
| 1 | a = int(input()) | 用户输入值 5 | 5 | ? |
| 2 | b = int(input()) | 用户输入值 7 | 5 | 7 |
| 3 | a = a + b | 12 | 7 | |
| 4 | b = a - b | 12 | 5 | |
| 5 | a = a - b | 7 | 5 | |
| 6 | print(a, b) | 显示:7 5 |
这种方法的不利之处在于它不能交换字母数字变量的内容(字符串)。
8.2 复习问题:对/错
Choose true or false for each of the following statements.
1)跟踪表是测试计算机的一种技术。
2)跟踪表有助于程序员找到计算机程序中的错误。
3)您不能在没有首先创建相应的跟踪表的情况下执行计算机程序。
4)为了交换两个整型变量的值,您始终需要一个额外的变量。
8.3 复习练习
完成以下练习。
1)创建一个跟踪表以确定当输入值为 3 时 Python 程序每一步的变量值。
a = float(input())
b = a + 10
a = b * (a - 3)
c = 3 * b / 6
d = c * c
d -= 1
print(d)
2)创建一个跟踪表以确定 Python 程序在三次不同执行中每一步的变量值。
三次执行的输入值分别为:(i) 3,(ii) 4,和(iii) 1。
a = int(input())
a = (a + 1) * (a + 1) + 6 / 3 * 2 + 20
b = a % 13
c = b % 7
d = a * b * c
print(a, ",", b, ",", c, ",", d)
3)创建一个跟踪表以确定 Python 程序在两次不同执行中每一步的变量值。
两次执行的输入值分别为:(i) 8,4;和(ii) 4,4
a = int(input())
b = int(input())
c = a + b
d = 1 + a / b * c + 2
e = c + d
c += d + e
e -= 1
d -= c + d % c
print(c, ",", d, ",", e)
第九章
使用 IDLE 或 Visual Studio Code
9.1 编写、执行和调试 Python 程序
到目前为止,您已经学习了关于 Python 编程的一些扎实的基础知识。现在是时候探索将程序输入计算机、执行它们、观察它们的性能、检查它们如何显示结果以及学习调试它们的技巧了。
调试是寻找并减少计算机程序中缺陷(错误)数量以使其按预期运行的过程。
如第 2.5 节所述,集成开发环境(IDE)是一种软件,它使程序员能够编写、执行和调试他们的源代码。例如,IDLE 和 Visual Studio Code。选择您打算使用的是您的事情。
关于如何在 Windows 或 Linux 上编写、执行和调试 Python 程序的所有说明都保存在我的网站上,以下地址。这使我能够经常审查它们并保持它们最新。
www.bouraspage.com/python-setup-write-execute-debug
.
如果您发现任何不一致之处,请告知我,我将尽快更新说明。要报告问题,请访问以下地址之一:
www.bouraspage.com/report-errata
.
第十四章:Review in “Getting Started with Python”
Review Crossword Puzzles
- 解决以下填字游戏。

Across
-
这些错误很难检测。
-
一个控制结构。
-
它显示了流程图中的执行流程。
-
展示算法的一种图形方法。
-
_______________ 编程是一种使用模块化和结构化设计的软件开发方法。
-
严格定义的有限序列的明确语句,提供了解决问题的方案。
-
术语 ________ 意味着算法必须达到一个终点,不能无限期运行。
-
这个流程图符号有一个入口和两个出口。
-
逻辑错误和运行时错误通常被称为 _______。
-
必须能够在有限的时间内正确执行算法的每个步骤。这是算法必须满足的一个属性,被称为 ________。
Down
-
最佳定义用户友好设计的原则。
-
这代表流程图中的数学(公式)计算。
-
数据 _______________ 是创建算法涉及的三个主要阶段之一。
-
在计算机语言中有严格预定义意义的单词。
-
一种编程语言。
-
语句。
-
使用程序的人。
-
一个控制结构。
-
Real.
-
算法必须满足的一个属性。
-
解决以下填字游戏。

Across
-
一个字母数字值。
-
拼写错误的关键字是一个 _________ 错误。
-
一个没有分数部分的正数或负数。
-
可以包含在程序中以使其更容易阅读和理解的信息。
-
这种类型的变量只能持有两个值之一。
-
在程序执行过程中发生的一个错误。
-
这种控制结构也被称为选择控制结构。
-
这是一个消耗 CPU 时间的算术操作。
-
在程序运行期间不能改变的值。
-
一个用户-________ 程序是一个对新手用户来说很容易的程序。
Down
-
一个 _________ 表是用于测试算法或计算机程序在执行过程中发生的逻辑错误的技术。
-
任何包含在 ___________ 中的算术运算首先执行。
-
流程图中的左箭头被称为值 _____________ 操作符。
-
允许在变量名称中使用的符号字符。
-
它代表计算机主内存(RAM)中的一个位置,程序可以在此处存储一个值。
-
在主内存(RAM)中为存储变量内容保留部分的过程。
-
将两个独立的字符串合并成一个。
-
在计算机程序中寻找和减少逻辑错误数量的过程。
-
模运算符返回整数除法的 ___________。
-
运算符(//)返回整数除法的 ___________。
Review Questions
答以下问题。
- 什么是算法?
2)给出制作咖啡的算法。
3)一个算法必须满足哪五个属性?
4)算法能否无限执行?
5)什么是计算机程序?
6)算法中涉及的三方是什么?
7)组成计算机程序的三个阶段是什么?
8)一个计算机程序能否由两个阶段组成?
9)什么是流程图?
10)流程图使用的基本符号有哪些?
11)“保留字”这个术语是什么意思?
12)什么是结构化编程?
13)结构化程序设计的三个基本控制结构是什么?
14)使用流程图给出每个控制结构的例子。
15)程序员能否在文本编辑器中编写 Python 程序?
16)什么是语法错误?给出一个例子。
17)什么是逻辑错误?给出一个例子。
18)什么是运行时错误?给出一个例子。
19)拼写错误的保留字会导致什么类型的错误?
20)“调试”这个术语是什么意思?
21)为什么程序员应该在他们的代码中添加注释?
22)为什么程序员应该编写用户友好的程序?
23)缩写 POLA 代表什么?
24)什么是变量?
25)在流程图的左箭头的左侧可以存在多少个变量?
26)变量的值存储在计算机的哪个部分?
27)什么是常量?
28)如何使用常量来帮助程序员?
29)为什么程序员应尽可能避免除法和乘法运算?
30)至少列出三种变量的数据类型。
31)“声明一个变量”这个短语是什么意思?
32)如何在 Python 中声明一个变量?给出一个例子。
33)在流程图中用来显示消息的符号是什么?
34)Python 中“换行”和“制表符”的特殊字符序列是什么?
35)在流程图中用来让用户输入数据的符号是什么?
36)Python 中用作值赋值运算符的字符是什么,在流程图中如何表示?
37)Python 支持哪些算术运算符?
38)什么是取模运算符?
39)总结算术运算符优先级的规则。
40)Python 支持哪些复合赋值运算符?
41)Python 支持哪些字符串运算符?
42)什么是跟踪表?
43)使用跟踪表有什么好处?
44)描述交换两个变量(无论是数字还是字母数字)内容所涉及的步骤。
45)本书提出了三种交换两个变量值的方法。哪一种更好,为什么?
46)描述 IDLE 如何帮助你找到语法错误。
47)描述 IDLE 如何帮助你找到逻辑错误。
48)描述 Visual Studio Code 如何帮助你找到语法错误。
49)描述 Visual Studio Code 如何帮助你找到逻辑错误。
第三部分
顺序控制结构
第十章
序列控制结构简介
10.1 什么是序列控制结构?
序列控制结构指的是按行执行的语句按其在程序中出现的顺序依次执行,不跳过任何一条。例如,它们可能执行一系列的读写操作、算术运算或变量的赋值。
以下程序展示了 Python 语句按顺序执行的示例。
file_10.1
提示用户输入 num 的值
num = int(input("Enter a number: "))
计算 num 的平方
result = num ** 2
在用户的屏幕上显示结果
print("The square of", num, "is", result)
序列控制结构是你在第 4.11 节中学习到的三个基本控制结构中最简单的一个。其他两个结构是“决策结构”和“循环结构”。计算机编程中的所有问题都可以仅使用这三个结构来解决!
在 Python 中,你可以使用井号字符(#)添加注释。注释是为了人类读者。编译器和解释器会忽略它们。
练习 10.1-1 计算矩形的面积
编写一个 Python 程序,提示用户输入矩形的底边长和高度,然后计算并显示其面积。
解答
你可能从学校就知道,你可以使用以下公式计算矩形的面积:
面积 = 底 × 高
在第 4.6 节中,你学习了创建算法涉及到的三个主要阶段:数据输入、数据处理和结果输出。
在这个练习中,这三个主要阶段如下:
►数据输入 – 用户必须输入底边长和高度的值
►数据处理 – 程序必须计算矩形的面积
►结果输出 – 程序必须显示上一阶段计算出的矩形面积。
这个问题的解答如下。
file_10.1-1
数据输入 - 提示用户输入底边长和高度的值
b = float(input("Enter the length of base: "))
h = float(input("Enter the length of height: "))
数据处理 - 计算矩形的面积
area = b * h
结果输出 - 在用户的屏幕上显示结果
print("The area of the rectangle is", area)
练习 10.1-2 计算圆的面积
编写一个 Python 程序,计算并显示圆的面积。
解答
你可以使用以下公式计算圆的面积:
面积 = π × 半径²
π的值是一个已知量,大约为 3.14159。因此,用户必须输入的唯一值是半径的值。
在这个练习中,你在第 4.6 节中学到的三个主要阶段如下:
►数据输入 – 用户必须输入半径的值
►数据处理 - 程序必须计算圆的面积
►结果输出 – 程序必须显示上一阶段计算出的圆面积。
这个问题的解决方案如下所示。
file_10.1-2a
数据输入 - 提示用户输入半径值
radius = float(input("请输入半径长度:"))
数据处理 - 计算圆的面积
area = 3.14159 * radius ** 2
结果输出 - 在用户屏幕上显示结果
print("圆的面积是", area)
使用一个常数,PI,会是一个更好的方法。
file_10.1-2b
PI = 3.14159
数据输入 - 提示用户输入半径值
radius = float(input("请输入半径长度:"))
数据处理 - 计算圆的面积
area = PI * radius ** 2
结果输出 - 在用户屏幕上显示结果
print("圆的面积是", area)
指数运算的优先级更高,会在乘法运算之前执行。
练习 10.1-3 汽车在哪里?计算行驶距离
汽车从静止开始,沿直线水平道路以恒定加速度行驶指定时间。编写一个 Python 程序,提示用户输入加速度和汽车行驶的时间,然后计算并显示行驶的距离。所需的公式是
.
其中
►S 是汽车行驶的距离,以米(m)为单位
►u[o] 是汽车的初始速度(速度),以每秒米(m/sec)为单位
►t 是汽车行驶的时间,以秒(sec)为单位
►a 是加速度,以每秒每秒²(m/sec²)为单位
解答
由于汽车是从静止开始的,初始速度(速度)u[0] 为零。因此,公式变为

并且 Python 程序是
file_10.1-3
a = float(input("请输入加速度:"))
t = float(input("请输入行驶时间:"))
S = 0.5 * a * t ** 2
print("你的车行驶了", S, "米")
指数运算的优先级更高,会在乘法运算之前执行。
练习 10.1-4 开氏度到华氏度
编写一个 Python 程序,将温度值从华氏度转换为开尔文等效值。所需的公式是
1.8 × 开尔文 = 华氏度 + 459.67
解答
给定的公式不能直接用于你的程序中。在像 Python 这样的计算机语言中,不允许直接写入
1.8 * kelvin = fahrenheit + 459.67
在等号(=)左侧的位置,只能存在一个变量。这个变量实际上是在 RAM 中可以存储值的区域。
根据这个练习的措辞,程序必须将华氏度转换为开尔文度。华氏度的值是一个已知值,由用户提供,而开尔文度的值是 Python 程序必须计算的。因此,你需要解出开尔文。经过一些工作,公式变为
.
Python 程序如下所示。
file_10.1-4
fahrenheit = float(input("输入华氏温度: "))
kelvin = (fahrenheit + 459.67) / 1.8
print("开尔文温度是:", kelvin)
练习 10.1-5 计算销售税
员工需要一个程序来输入产品的不含税价格并计算其最终价格。假设增值税(VAT)率为 19%。
解决方案
销售税可以很容易地计算。你必须将产品的不含税价格乘以 VAT 率。注意——销售税不是最终价格,只是税额。
通过将初始不含税价格和之前计算的销售税相加,可以计算出含税价格。
在这个程序中,你可以使用一个名为 VAT 的常量来表示销售税率(VAT 税率)。
file_10.1-5
VAT = 0.19
priceBeforeTax = float(input("输入不含税价格: "))
salesTax = priceBeforeTax * VAT
priceAfterTax = priceBeforeTax + salesTax
print("含税价格是:", priceAfterTax)
练习 10.1-6 计算销售折扣
编写一个 Python 程序,提示用户输入商品的价格和提供的折扣率(在 0 到 100 的范围内)。然后程序必须计算并显示新的价格。
解决方案
折扣金额可以很容易地计算。你必须将产品的原价乘以折扣值,然后除以 100。除法是必要的,因为用户在 0 到 100 的范围内输入折扣值。注意——结果不是最终价格,只是折扣金额。
最终的折扣后价格可以通过从初始不含折扣价格中减去之前计算的折扣金额来计算。
file_10.1-6
priceBeforeDiscount = float(input("输入产品的价格: "))
discount = int(input("输入提供的折扣(0 - 100): "))
discountAmount = priceBeforeDiscount * discount / 100
priceAfterDiscount = priceBeforeDiscount - discountAmount
print("折扣后的价格是:", priceAfterDiscount)
练习 10.1-7 计算销售折扣和税
编写一个 Python 程序,提示用户输入商品的不含税价格和提供的折扣率(在 0 到 100 的范围内)。然后程序必须计算并显示新的价格。假设销售税率为 19%。
解决方案
这个练习只是前两个练习的组合!
file_10.1-7
VAT = 0.19
priceBeforeDiscount = float(input("输入产品的价格: "))
discount = int(input("输入提供的折扣(0 - 100):"))
discountAmount = priceBeforeDiscount * discount / 100
priceAfterDiscount = priceBeforeDiscount - discountAmount
salesTax = priceAfterDiscount * VAT
priceAfterTax = priceAfterDiscount + salesTax
print("折扣后的税后价格是:", priceAfterTax)
10.2 复习练习
完成以下练习。
1)在美国,汽车的燃油经济是以每加仑英里数(MPG)来衡量的。汽车的 MPG 可以使用以下公式计算:
.
编写一个 Python 程序,提示用户输入他们已行驶的总英里数和使用的汽油加仑数。然后程序必须计算并显示汽车的每加仑英里数(MPG)。
2)编写一个 Python 程序,提示用户输入底边和高的值,然后计算并显示三角形的面积。所需的公式是
.
3)编写一个 Python 程序,提示用户输入三角形的两个角度,然后计算并显示第三个角度。
提示:任何三角形的内角和为 180 度
4)编写一个 Python 程序,允许学生输入他们的四次考试成绩,然后计算并显示平均成绩。
5)编写一个 Python 程序,提示用户输入半径的值,然后计算并显示圆的周长。所需的公式是
周长 = 2πR
6)编写一个 Python 程序,提示用户输入直径的值(以米为单位),然后计算并显示球体的体积。所需的公式是
.
其中 R 是球体的半径。
7)关于前面的练习,以下哪些结果输出语句是正确的?你会选择哪一个来在用户的屏幕上显示球体的体积,为什么?
a)print(V)
b)print(V 立方米)
c)print(V, 立方米)
d)print("球体的体积是:" V)
e)print("球体的体积是:", V)
f)print("球体的体积是:" V, 立方米)
g)print("球体的体积是:", V, "立方米")
8)编写一个 Python 程序,提示用户输入他们的名字、中间名、姓氏和他们的首选头衔(先生、夫人、女士、博士等),并以以下所有方式格式化显示它们。
标题:FirstName MiddleName LastName
FirstName MiddleName LastName
LastName, FirstName
LastName, FirstName MiddleName
LastName, FirstName MiddleName, Title
FirstName LastName
例如,假设用户输入以下内容:
名字:Aphrodite
中间名:Maria
姓氏:Boura
标题:Ms.
程序必须以以下所有方式格式化显示用户的名字:
Ms. Aphrodite Maria Boura
Aphrodite Maria Boura
Boura, Aphrodite
Boura, Aphrodite Maria
Boura, Aphrodite Maria, Ms.
Aphrodite Boura
9)编写一个 Python 程序,提示用户输入直径的值,然后计算并显示圆的半径、周长和面积。对于相同的直径,它还必须显示球体的体积。
10)编写一个 Python 程序,提示用户输入餐厅餐费的金额,然后计算并显示 10% 小费、7% 销售税和所有三项金额的总计。
11)一辆汽车从静止开始,沿直线水平道路以恒定加速度行驶指定时间。编写一个 Python 程序,提示用户输入行驶的距离以及行驶的分钟和秒,然后计算加速度。所需的公式是
.
其中
►S 是汽车行驶的距离,以米(m)为单位
►u[o] 是汽车的初始速度(速度),以每秒米(m/sec)为单位
►t 是汽车行驶的时间,以秒(sec)为单位
►a 是加速度,以每秒平方米(m/sec²)为单位
12)编写一个 Python 程序,提示用户输入华氏温度,然后将其转换为摄氏度^([10]) 等效值。所需的公式是

13)身体质量指数(BMI)常用于确定一个人是否因为身高而超重或体重不足。计算 BMI 所使用的公式是
.
编写一个 Python 程序,提示用户输入他们的体重(以磅为单位)和身高(以英寸为单位),然后计算并显示用户的 BMI。
14)编写一个 Python 程序,提示用户输入小计和消费税率(0 到 100 的范围内),然后计算小费和总计。例如,如果用户输入 30 和 10,Python 程序必须显示“小费是 $3.00,总计是 $33.00”。
15)一名员工需要一个程序来输入三个产品的税前价格,然后计算每个产品的最终税后价格以及它们的平均价值。假设增值税(VAT)率为 20%。
16)一名员工需要一个程序来输入产品的税后价格,然后计算其税前价格。假设增值税(VAT)率为 20%。
17)编写一个 Python 程序,提示用户输入商品的初始价格和提供的折扣率(0 到 100 的范围内),然后计算并显示最终价格和节省的金额。
18)编写一个 Python 程序,提示用户输入一个月开始和结束时的电表读数(千瓦时 kWh)。程序必须根据每千瓦时 $0.06 的成本和 20% 的增值税(VAT)率计算并显示消耗的千瓦时数和必须支付的金额。
19)游艇工厂经理需要一个程序来计算工厂在一年期间获得的利润或亏损。以下是一些信息:
►建造一艘游艇的成本是工厂 1,000,000 美元。
►每艘游艇售价为 150 万美元。
►该工厂每月支付 25 万美元的保险费。
编写一个 Python 程序,提示用户输入售出的游艇数量,然后计算并显示相应的总收入或亏损,以正数或负数表示。
20)编写一个 Python 程序,提示用户输入当前月份和当前月内的日期,然后计算并显示从年初至今已过去的天数。假设每个月有 30 天。
21)编写一个 Python 程序,提示用户输入当前月份和当前月内的日期,然后计算并显示到年底的天数。假设每个月有 30 天。
第十六章:Chapter 11
操作数字
11.1 简介
就像每种高级编程语言一样,Python 提供了许多随时可用且可在任何地方使用的函数和方法(称为子程序)。

where
►y 是你想要找到平方根的数字
►x[n] 是 y 的平方根的第 n 次迭代值
你现在可能感到有些沮丧。你可能认为你应该编写一个程序来计算海伦公式以找到数字的平方根,但事实并非如此!目前,没有人这样计算数字的平方根。幸运的是,Python 包含了一个用于此目的的函数!这个函数实际上是一个小的子程序,被命名为 sqrt,你只需要通过它的名字来调用它,它就会为你完成工作。sqrt 函数可能使用海伦迭代公式,或者可能是来自古代或现代数学家的公式。事实是,你并不真的关心!真正重要的是 sqrt 给你正确的结果!这里有一个示例。
from math import sqrt
x = float(input())
y = sqrt(x)
print(y)
函数 sqrt() 定义在 math 模块中。在 Python 中不能直接访问,所以你需要从 math 模块中导入它。
在 Python 中,“模块”不过是一个包含许多随时可用函数(或方法)的文件。Python 集成了相当多的这样的模块,但如果你希望使用这些模块中的一个包含的函数,你需要将其从该模块导入到你的程序中。
尽管 Python 支持许多数学函数(和方法),但本章仅涵盖本书目的绝对必要的那些。然而,如果你需要更多信息,你可以访问以下地址之一:
docs.python.org/3.13/library/math.html

数学子程序在需要执行数学计算时使用,例如求平方根、正弦、余弦、绝对值等。
11.2 有用的数学函数(子程序),以及更多
绝对值
abs(数字)
这个函数返回数字的绝对值。
示例
file_11.2a
a = -5
b = abs(a)
print(abs(a)) #它显示:5
print(b) #它显示:5
print(abs(-5.2)) #它显示:5.2
print(abs(5.2)) #它显示:5.2
π
pi
这包含了 π 的值。
示例
file_11.2b
from math import pi
print(pi) #它显示:3.141592653589793
注意,pi 既不是函数也不是方法。因此,您不能在末尾放置括号。
定义在 math 模块中。在 Python 中不能直接访问,所以需要从 math 模块中导入。
正弦
sin(number)
此函数返回数字的正弦值。数字的值必须以弧度表示。您可以通过乘以 pi / 180 将度转换为弧度。
示例
file_11.2c
from math import sin, pi
a = sin(3 * pi / 2) #3π/2 弧度的正弦值
b = sin(270 * pi / 180) #270 度的正弦值
print(a, b) #它显示:-1.0 -1.0
函数 sin()定义在 math 模块中。在 Python 中不能直接访问,所以需要从 math 模块中导入。
余弦
cos(number)
此函数返回数字的余弦值。数字的值必须以弧度表示。您可以通过乘以 pi / 180 将度转换为弧度。
示例
file_11.2d
from math import cos, pi
a = cos(2 * pi) #2π弧度的余弦值
b = cos(360 * pi / 180) #360 度的余弦值
print(a, b) #它显示:1.0 1.0
函数 cos()定义在 math 模块中。在 Python 中不能直接访问,所以需要从 math 模块中导入。
正切
tan(number)
此函数返回数字的正切值。数字的值必须以弧度表示。您可以通过乘以 pi / 180 将度转换为弧度。
示例
file_11.2e
from math import tan ,pi
a = tan(10 * pi / 180) #10 度的正切值
print(a) #它显示:0.176326980708
函数 tan()定义在 math 模块中。在 Python 中不能直接访问,所以需要从 math 模块中导入。
整数除法的商和余数
divmod(number1, number2)
此函数返回两个值,这些值是:
►number1 和 number2 整数除法的商;以及
►number1 和 number2 整数除法的余数
示例
file_11.2f
c, d = divmod(13, 4)
print(c, d, sep = ", ") #它显示:3, 1
在 Python 中,一个函数或方法可以返回两个或更多值。
整数值
int(value)
此函数返回数值作为整数,即值的整数部分。如果值包含小数部分,则在转换过程中该部分将丢失。
如果值包含整数的字符串表示,则返回其数值等效。
示例
file_11.2g
a = 5.4
print(int(a)) #它显示:5
print(int(34)) #它显示:34
print(int(34.9)) #它显示:34
print(int(-34.999)) #它显示:-34
s1 = "5"
s2 = "3"
k = int(s1)
print(k) #它显示:5
print(int(s2)) #显示:3
print(s1 + s2) #显示:53
print(k + int(s2)) #显示:8
实数值
float(值)
此函数将值作为浮点数(实数)返回。
如果值包含整数的字符串表示或浮点数(实数),则返回其数值等效。
示例
file_11.2h
a = 5
print(float(a)) #显示:5.0
print(float(34)) #显示:34.0
print(float(-34)) #显示:-34.0
print(float(34.9)) #显示:34.9
s1 = "6"
s2 = "3.4"
x = float(s1)
print(x) #显示:6.0
print(float(s2)) #显示:3.4
print(s1 + s2) #显示:63.4
print(x + float(s2)) #显示:9.4
范围
range([初始值,] 最终值 [, 步长])
此函数返回从初始值到最终值 - 1 之间的整数序列。初始值参数是可选的。如果省略,则默认值为 0。步长参数是序列中每个数字之间的差值。此参数也是可选的。如果省略,则默认值为 1。
请注意,初始值、最终值和步长必须是整数。也允许负值!
示例
file_11.2i
将序列 1, 2, 3, 4, 5 赋值给 x
x = range(1, 6)
将序列 0, 1, 2, 3, 4, 5 赋值给 y
y = range(6)
将序列 0, 10, 20, 30, 40 赋值给 w
w = range(0, 50, 10)
将序列 100, 95, 90, 85 赋值给 z
z = range(100, 80, -5)
随机数
randrange([最小值,] 最大值 [, 步长])
此函数返回给定范围内的随机整数。randrange() 函数的参数与 range() 函数的参数逻辑相同。
示例
file_11.2j
from random import randrange
显示 10 到 100 之间的随机整数
print(randrange(10, 101))
将 0 到 10 之间的随机整数赋值给变量 y 并显示它
y = randrange(11)
print(y)
显示 -20 到 20 之间的随机整数
print(randrange(-20, 21))
显示 1 和 99 之间的随机奇数
print(randrange(1, 99, 2))
显示 0 到 100 之间的随机偶数
print(randrange(0, 100, 2))
随机数在计算机游戏中被广泛使用。例如,“敌人”可能在随机时间出现或随机移动。此外,随机数还用于模拟程序、统计程序、计算机安全中的数据加密等。
randrange() 函数定义在 random 模块中。在 Python 中无法直接访问,因此您需要从 random 模块中导入它。
四舍五入
round(数字)
此函数返回数字的最接近整数。
示例
file_11.2k
a = 5.9
print(round(a)) #显示:6
print(round(5.4)) #显示:5
如果您需要将数字四舍五入到指定的精度,可以使用以下公式:
round(number * 10 ** precision) / 10 ** precision
示例
file_11.2l
a = 5.312
y = round(a * 10 ** 2) / 10 ** 2
print(y) #它显示:5.31
a = 5.315
y = round(a * 10 ** 2) / 10 ** 2
print(y) #它显示:5.32
print(round(2.3447 * 10 ** 3) / 10 * 10 ** 3) #它显示:2.345
print(round(2.3447 * 1000) / 1000) #它显示:2.345
平方根
sqrt(number)
此函数返回数字的平方根,其中数字可以是正数或零。
示例
file_11.2m
from math import sqrt
print(sqrt(9)) #它显示:3.0
print(sqrt(2)) #它显示:1.4142135623730951
x = sqrt(8)
print(x) #它显示:2.8284271247461903
y = round(sqrt(8))
print(y) #它显示:3
sqrt() 函数定义在 math 模块中。它不能直接在 Python 中访问,因此你需要从 math 模块中导入它。
注意函数 sqrt() 如何嵌套在函数 round() 中。内部(嵌套)函数(或函数)的结果用作外部函数的参数。这是大多数程序员更喜欢遵循的写作风格,因为它有助于节省大量的代码行。当然,如果你嵌套太多的函数,没有人会理解你的代码。最多嵌套四层是相当可接受的。
总和
fsum(sequence)
此函数返回序列中元素的总和。
示例
file_11.2n
from math import fsum
seq = [5.5, 6.3, 2] #将一组数字赋值给 seq
print(fsum(seq)) #它显示:13.8
fsum() 函数定义在 math 模块中。它不能直接在 Python 中访问,因此你需要从 math 模块中导入它。
练习 11.2-1 计算两点之间的距离
编写一个 Python 程序,提示用户输入两个点的坐标 (x, y),然后计算它们之间的直线距离。所需的公式是
.
解答
在这个练习中,你需要使用函数 sqrt(),它返回一个数的平方根。
为了简化问题,将 (x[1] - x[2])² 和 (y[1] - y[2])² 分别计算,并将结果分配给两个临时变量。Python 程序如下所示。
file_11.2-1a
from math import sqrt
print("输入点 A 的坐标:")
x1, y1 = float(input()), float(input())
print("输入点 B 的坐标:")
x2, y2 = float(input()), float(input())
xTemp = (x1 - x2) ** 2
yTemp = (y1 - y2) ** 2
d = sqrt(xTemp + yTemp)
print("两点之间的距离:", d)
现在,让我们看看另一种方法。实际上,可以在函数调用内执行所有操作。这样做,操作的结果将被用作函数的参数。这种写作风格是大多数程序员更喜欢遵循的,因为它可以节省很多变量和代码行。Python 程序如下所示。
file_11.2-1b
from math import sqrt
输入点 A 的坐标:
x1, y1 = float(input()), float(input())
print("输入点 B 的坐标:")
x2, y2 = float(input()), float(input())
d = sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
print("两点之间的距离:", d)
练习 11.2-2 汽车行驶了多远?
一辆汽车从静止开始,以恒定加速度沿直线水平道路行驶一段指定距离。编写一个 Python 程序,提示用户输入加速度和汽车行驶的距离,然后计算行驶时间。所需的公式是
.
其中
►S 是汽车行驶的距离,以米(m)为单位
►u[0] 是汽车的初始速度(速度),以每秒米(m/sec)为单位
t 是汽车行驶的时间,以秒(sec)为单位
►a 是加速度,以每秒平方米(m/sec²)为单位
解答
由于汽车是从静止开始的,初始速度(速度)u[0] 是零。因此,公式变为
.
现在,如果你求解时间,最终公式变为
.
在 Python 中,你可以使用 sqrt() 函数,它返回一个数字的平方根。
file_11.2-2
from math import sqrt
a = float(input("输入加速度: "))
S = float(input("输入行驶距离: "))
t = sqrt(2 * S / a)
print("汽车行驶了", t, "秒")
11.3 复习问题:正确/错误
对以下每个语句选择正确或错误。
1)一般来说,函数是解决小问题的小程序。
2)每个程序员都必须使用赫伦迭代公式来计算正数的平方根。
3)abs() 函数返回一个项目的绝对位置。
4)语句 int(3.59) 返回的结果是 3.6。
5)语句 y = int("two") 是一个有效的 Python 语句。
6)语句 y = int("2") 是一个有效的 Python 语句。
7)语句 int(3) 返回的结果是 3.0。
8)语句 float(3) 返回的结果是 3.0。
9)语句 y = float("3.14") 不是一个有效的 Python 语句。
10)math 模块中 pi 的值等于 3.14。
11)randrange() 函数可以返回负随机数。
12)语句 y = randrange(0, 2) 有 50% 的可能性将值 1 赋给变量 y。
13)语句 round(3.59) 返回的结果是 4。
14)要计算 90 度的正弦值,你必须编写 y = sin(pi / 2)
15)语句 y = sqrt(−2) 是有效的。
16)以下代码片段满足确定性的性质。
from math import sqrt
a, b = float(input()), float(input())
x = a * sqrt(b)
print(x)
11.4 复习问题:多项选择题
选择以下每个陈述的正确答案。
1)当执行语句 y = abs(+5.2) 时,变量 y 的值是多少?
a)−5.2
b)−5
c)0.2
d)5.2
e)以上皆非
2)以下哪个计算了 180 度的正弦值?
a)sin(180)
b)sin(pi)
c)所有上述选项
d)以上皆非
3)当执行语句 y = int(5 / 2) 时,变量 y 的值是多少?
a)2.5
b)3
c)2
d)0.5
4)当执行语句 y = sqrt(4) ** 2 时,变量 y 的值是多少?
a)4
b)2
c)8
d)16
5)当执行语句 y = round(5.2) / 2 时,变量 y 的值是多少?
a)2
b)2.5
c)2.6
d)以上皆非
11.5 复习练习
完成以下练习。
1)创建一个跟踪表,以确定 Python 程序在两次不同执行中每一步的变量值。
两次执行的输入值分别是:(i) 9,和 (ii) 4.
from math import sqrt
a = float(input())
a += 6 / sqrt(a) * 2 + 20.4
b = round(a) % 4
c = b % 3
print(a, ",", b, ",", c)
2)创建一个跟踪表,以确定 Python 程序在两次不同执行中每一步的变量值。
两次执行的输入值分别是:(i) −2,和 (ii) −3
a = int(input())
b = abs(a) % 4 + a ** 4
c = b % 5
print(b, ",", c)
3)编写一个 Python 程序,提示用户输入以弧度表示的角 θ,然后计算并显示该角度的度数。已知 2π = 360°。
4)编写一个 Python 程序,提示用户输入直角三角形的两个直角边 A 和 B,然后计算其斜边。根据勾股定理([12]),已知
.
5)编写一个 Python 程序,提示用户输入直角三角形的角 θ(以度为单位)和相邻边的长度,然后计算对边的长度。已知
.
第十二章
复杂的数学表达式
12.1 编写复杂的数学表达式
在 第 7.2 节 中,你学习了所有关于算术运算符的内容,但很少了解如何使用它们以及如何编写自己的复杂数学表达式。在本章中,你将了解到将数学表达式转换为 Python 语句是多么容易。
算术运算符遵循与数学中相同的优先级规则,这意味着指数运算首先执行,然后是乘法和除法,最后是加法和减法。此外,当同一个表达式中存在乘法和除法时,由于它们的优先级相同,这些运算从左到右执行。
指数运算符(**)具有双重作用。除了用于计算一个值被提升到另一个值的幂之外,它还用于使用已知的数学公式
计算一个数的任何根。例如,你可以写 y = x ** (1 / 3) 来计算 x 的立方根,或者 y = x ** (1 / 5) 来计算 x 的五次根。
练习 12.1-1 在 Python 中表示数学表达式
以下哪个 Python 语句正确地表示了以下数学表达式?
.
i)x = 1 * 27 / 10 + z
ii)x = 1 · 27 / (10 + z)
iii)x = 27 / 10 + z
iv)x = 27 / (10 + z)
v)x = (1 / 10 + z) * 27
vi)x = 1 / ((10 + z) * 27)
vii)x = 1 / (10 + z) * 27
viii)x = 1 / (10 + z) / 27
解决方案
i) 错误。由于乘法和除法在加法之前执行,这相当于
。
ii) 错误。必须使用星号进行乘法。
iii) 错误。由于除法在加法之前执行,这相当于
。
iv) 正确。这相当于
。
v) 错误。在括号内,除法在加法之前执行。这相当于
。
vi) 错误。括号内的运算首先执行,这相当于 
vii) 正确。除法在乘法之前执行(从左到右)。首先计算
,然后,将结果乘以 27。
viii) 错误。这相当于 
练习 12.1-2 在 Python 中编写数学表达式
编写一个 Python 程序来计算数学表达式
.
解决方案
首先,你必须区分数据输入和输出结果。显然,输出结果分配给 y,用户必须输入 x 和 z 的值。这个练习的解决方案如下所示。
file_12.1-2
x = float(input())
z = float(input())
y = 10 * x - (10 - z) / 4
print("结果是:", y)
练习 12.1-3 在 Python 中编写复杂的数学表达式
编写一个 Python 程序,计算以下数学表达式
.
假设用户只为 x、w 和 z 输入正值。
解答
哎呀!现在表达式更复杂了!实际上,它要复杂得多!所以,让我们看看一个相当不同的方法。主要思想是将复杂表达式分解成更小、更简单的表达式,并将每个子结果分配给临时变量。最后,你可以用所有这些临时变量构建原始表达式!这种方法将在下面介绍。
file_12.1-3a
x = float(input())
w = float(input())
z = float(input())
temp1 = 3 * x ** 2 + 5 * x + 2
temp2 = 7 * w + 1 / z
temp3 = (3 + x) / 7
分子 = 5 * temp1 / temp2 + z
分母 = 4 * temp3
y = 分子 / 分母
print("结果是:", y)
你可能会说,“好吧,但我浪费了这么多变量,而且众所周知,每个变量都是主内存的一部分。我如何将原始表达式写在一行中并浪费更少的内存?”
这项工作可能对高级程序员来说是小菜一碟,但对你呢?对于一个新手程序员呢?
下一种方法将帮助您编写即使是最复杂的数学表达式也不会出现任何语法或逻辑错误!规则非常简单。“在将复杂表达式分解成更小、更简单的表达式并将每个子结果分配给临时变量之后,从后往前开始,用分配的表达式替换每个变量。但要注意!当你用分配的表达式替换一个变量时,你必须始终将表达式放在括号内!”
感到困惑吗?不要!实际上操作起来更容易。让我们尝试重写之前的 Python 程序。从后往前,用分配的表达式替换变量分子和分母。结果是

注意添加的额外括号。
现在,你必须将变量 temp1、temp2 和 temp3 替换为它们分配的表达式,这样一行表达式就完成了!

虽然最后看起来可能很吓人,但其实并不难,不是吗?
现在可以将 Python 程序重写为
file_12.1-3b
x = float(input())
w = float(input())
z = float(input())
y = (5 * (3 * x ** 2 + 5 * x + 2) / (7 * w + 1 / z) + z) / (4 * ((3 + x) / 7))
print("结果是:", y)
12.2 复习练习
完成以下练习。
1)将第一个表中的每个元素与第二个表中的一个或多个元素匹配。
| 表达式 | 表达式 |
|---|---|
| i) 5 / x ** 2 * y + x ** 3 | a) 5 * y / x ** 2 + x ** 3 |
| ii) 5 / (x ** 3 * y) + x ** 2 | b) 5 * y / x * x + x ** 3 |
| c) 5 / (x * x * x * y) + x * x | |
| d) 5 / (x * x * x) * y + x * x | |
| e) 5 * y / (x * x) + x * x * x | |
| f) 1 / (x * x * x * y) * 5 + x * x | |
| g) y / (x * x) * 5 + x ** 3 | |
| h) 1 / (x * x) * 5 * y + x / 1 * x * x |
2)使用一行代码将以下数学表达式在 Python 中编写出来。
i)
ii)
iii)
iv)
v)
vi)
3)编写一个 Python 程序,提示用户输入 x 的值,然后计算并显示以下数学表达式的结果。
.
4)编写一个 Python 程序,提示用户输入 x 的值,然后计算并显示以下数学表达式的结果。
.
建议:尝试将表达式写在一行代码中。
5)编写一个 Python 程序,提示用户输入 x 和 w 的正值,然后计算并显示以下数学表达式的结果。
.
建议:尝试将表达式写在一行代码
6)编写一个 Python 程序,提示用户输入 x 和 w 的正值,然后计算并显示以下数学表达式的结果。
.
建议:尝试将表达式写在一行代码。
7)编写一个 Python 程序,提示用户输入 x 和 w 的正值,然后计算并显示以下数学表达式的结果。
.
建议:尝试将表达式写在一行代码
8)编写一个 Python 程序,提示用户输入三角形的三边长度 A、B 和 C,然后计算并显示三角形的面积。你可以使用近 2000 年前就为人所知的海伦公式!
.
其中 S 是半周长 
第十三章
带有商和余数的练习
13.1 简介
在整数除法中,可能需要使用商和余数的哪些类型的问题?虽然这个问题可能没有简单的答案,但商和余数可以用来:
►将一个数字拆分为单独的数字
►检查一个整数是奇数还是偶数
►检查一个数是否是另一个数的倍数
►将经过的时间(以秒为单位)转换为小时、分钟和秒
►将金额(以美元为单位)转换为 100 美元纸币、50 美元纸币、20 美元纸币等数量
►计算最大公约数
►确定一个数是否是回文数
►计算一个数字中数字的数量
►确定一个特定数字在数字中出现的次数
当然,这些只是一些用途,你肯定可以找到更多。接下来,你将看到一些利用整数除法商和余数的练习。
练习 13.1-1 计算整数除法的商和余数
编写一个 Python 程序,提示用户输入两个整数,然后计算整数除法的商和余数。
解答
你可以使用 Python 的 ( // ) 和 ( % ) 运算符。前者执行整数除法并返回整数商,而后者执行整数除法并返回整数余数。解决方案在此处展示。
file_13.1-1a
number1 = int(input("Enter first number: "))
number2 = int(input("Enter second number: "))
q = number1 // number2
r = number1 % number2
print("Integer Quotient:", q, "\nInteger Remainder:", r)
在流程图中,为了计算整数除法的商和余数,你可以使用流行的 DIV 和 MOD 运算符。以下是一个示例。
.
更“Pythonic”的方法是使用 divmod() 函数,如下面的示例所示。
file_13.1-1b
number1 = int(input("Enter first number: "))
number2 = int(input("Enter second number: "))
q, r = divmod(number1, number2)
print("Integer Quotient:", q, "\nInteger Remainder:", r)
练习 13.1-2 求数字之和
编写一个 Python 程序,提示用户输入一个四位数,然后计算其数字之和。
解答
在这里你应该记住的是,像这样的语句
number = int(input("Enter a four-digit integer: "))
将用户提供的四位数分配给一个单独的变量,number,而不是分配给四个单独的变量。因此,在用户输入四位数后,程序必须将整数拆分为其四个数字,并将每个数字分配给一个单独的变量。然后它可以计算这些四个变量的和,得到所需的结果。有两种方法可用。
第一种方法
让我们尝试通过一个算术示例来理解第一种方法。以数字 6753 为例。
| 第一位数字 = 6 | 如果使用 ( // ) 运算符将用户提供的数字除以 1000,则可以隔离第一位数字 digit1 = 6753 // 1000 |
|---|---|
| 剩余数字 = 753 | 如果再次将用户提供的数字除以 1000,这次使用 ( % ) 运算符来获取整数余数 r = 6753 % 1000,则可以隔离剩余的数字 |
| 第二位数字 = 7 | 如果使用 ( // ) 运算符将剩余的数字除以 100,则可以隔离第二位数字 digit2 = 753 // 100 |
| 剩余数字 = 53 | 现在的剩余数字是 r = 753 % 100 |
| 第三位数字 = 5 | 如果使用 ( // ) 运算符将剩余的数字除以 10,则可以隔离第三位数字 digit3 = 53 // 10 |
| 第四位数字 = 3 | 最后剩下的数字,恰好是第四位数字,是 digit4 = 53 % 10 |
解决此算法的 Python 程序如下所示。
file_13.1-2a
number = int(input("输入一个四位整数:"))
digit1 = number // 1000
r = number % 1000
digit2 = r // 100
r = r % 100
digit3 = r // 10
digit4 = r % 10
total = digit1 + digit2 + digit3 + digit4
print(total)
你刚才看到的程序的跟踪表如下所示。
| 步骤 | 语句 | 备注 | number | digit1 | digit2 | digit3 | digit4 | r | total |
|---|---|---|---|---|---|---|---|---|---|
| 1 | number = int(input("Enter … | 用户输入 6753 | 6753 | ? | ? | ? | ? | ? | ? |
| 2 | digit1 = number // 1000 | 6753 | 6 | ? | ? | ? | ? | ? | |
| 3 | r = number % 1000 | 6753 | 6 | ? | ? | ? | 753 | ? | |
| 4 | digit2 = r // 100 | 6753 | 6 | 7 | ? | ? | 753 | ? | |
| 5 | r = r % 100 | 6753 | 6 | 7 | ? | ? | 53 | ? | |
| 6 | digit3 = r // 10 | 6753 | 6 | 7 | 5 | ? | 53 | ? | |
| 7 | digit4 = r % 10 | 6753 | 6 | 7 | 5 | 3 | 53 | ? | |
| 8 | total = digit1 + digit2 + digit3 + digit4 | 6753 | 6 | 7 | 5 | 3 | 53 | 21 | |
| 9 | print(total) | 它显示:21 |
为了进一步帮助你,下面是一个通用的 Python 程序,可以用来分割任何给定的整数。由于程序长度取决于数字的个数 N,你只需要编写 N-1 对语句。
number = int(input("Enter an N-digit integer: "))
digit1 = number // 10^(N-1)
r = number % 10^(N-1)
digit2 = r // 10^(N-2)
r = r % 10^(N-2)
.
.
digit(N-2) = r // 100
r = r % 100
digit(N-1) = r // 10
digit(N) = r % 10
例如,如果你想分割一个六位整数,你需要编写五对如程序中所示的状态语句。
file_13.1-2b
number = int(input("输入一个六位整数:"))
digit1 = number // 100000
r = number % 100000
digit2 = r // 10000
r = r % 10000
digit3 = r // 1000
r = r % 1000
digit4 = r // 100
r = r % 100
digit5 = r // 10
digit6 = r % 10
print(digit1, digit2, digit3, digit4, digit5, digit6)
这种方法,然而,可以通过使用 divmod() 函数进行一点改进。
file_13.1-2c
number = int(input("请输入一个六位数整数:"))
digit1, r = divmod(number, 100000)
digit2, r = divmod(r, 10000)
digit3, r = divmod(r, 1000)
digit4, r = divmod(r, 100)
digit5, digit6 = divmod(r, 10)
print(digit1, digit2, digit3, digit4, digit5, digit6)
第二种方法
对于一个四位数的给定整数,第一种方法执行了三对除法——首先除以 1000,然后除以 100,最后除以 10——从左到右隔离数字。相比之下,第二种方法中的三对除法都是除以 10,从右到左隔离数字。再次深入探讨这种方法,让我们用一个算术例子来说明。考虑相同的用户提供的数字:6753。
| 第四位数字 = 3 | 如果您使用(%)运算符将用户提供的数字除以 10,以获取整数余数 digit4 = 6753 % 10,可以隔离第四位数字 |
|---|---|
| 剩余数字 = 675 | 如果您再次将用户提供的数字除以 10,这次使用(//)运算符来获取整数商 r = 6753 // 10,可以隔离剩余的数字 |
| 第三位数字 = 5 | 如果您使用(%)运算符将剩余的数字除以 10,以获取整数余数 digit3 = 675 % 10,可以隔离第三位数字 |
| 剩余数字 = 67 | 现在的剩余数字是 r = 675 // 10 |
| 第二位数字 = 7 | 如果您使用(%)运算符将剩余的数字除以 10,以获取整数余数 digit2 = 67 % 10,可以隔离第二位数字 |
| 第一位数字 = 6 | 最后剩下的数字,恰好是第一位数字,是 digit1 = 67 // 10 |
这里展示了该算法的 Python 程序。
file_13.1-2d
number = int(input("请输入一个四位数整数:"))
digit4 = number % 10
r = number // 10
digit3 = r % 10
r = r // 10
digit2 = r % 10
digit1 = r // 10
total = digit1 + digit2 + digit3 + digit4
print(total)
为了进一步帮助您,下面是一个通用的 Python 程序,可以用来分割任何给定的整数。这个程序使用第二种方法。同样,由于您程序的长度取决于数字的个数 N,您只需编写 N-1 对语句。
number = int(input("请输入一个 N 位数整数:"))
digit(N) = number % 10
r = number // 10
digit(N-1) = r % 10
r = r // 10
.
.
.
digit3 = r % 10
r = r // 10
digit2 = r % 10
digit1 = r // 10
例如,如果您想分割一个五位数整数,您必须使用程序中显示的四对语句。
file_13.1-2e
number = int(input("请输入一个五位数整数:"))
digit5 = number % 10
r = number // 10
digit4 = r % 10
r = r // 10
digit3 = r % 10
r = r // 10
digit2 = r % 10
digit1 = r // 10
print(digit1, digit2, digit3, digit4, digit5)
与前面的例子一样,这种方法可以稍微改进,使用 divmod()函数。
file_13.1-2f
number = int(input("请输入一个五位数整数:"))
r, digit5 = divmod(number, 10)
r, digit4 = divmod(r, 10)
r, digit3 = divmod(r, 10)
digit1, digit2 = divmod(r, 10)
print(digit1, digit2, digit3, digit4, digit5)
练习 13.1-3 显示经过的时间
编写一个 Python 程序,提示用户输入一个表示经过时间的整数(以秒为单位),然后以“DD 天 HH 小时 MM 分钟和 SS 秒”的格式显示它。例如,如果用户输入数字 700005,则必须显示消息“8 天 2 小时 26 分钟和 45 秒”。
解答
如您可能已经知道,一分钟有 60 秒,一小时有 3600 秒(60 × 60),一天有 86400 秒(3600 × 24)。让我们尝试使用您在之前练习中学到的第一种方法来分析数字 700005。
| 天数 = 8 | 如果您使用整数除法运算符(//)将用户提供的整数除以 86400,可以得到天数 days = 700005 // 86400,从而可以单独计算出天数 |
|---|---|
| 剩余秒数 = 8805 | 如果您再次将用户提供的整数除以 86400,这次使用取模运算符(%),可以得到余数 r = 700005 % 86400,从而可以单独计算出剩余秒数 |
| 小时数 = 2 | 如果您使用整数除法运算符(//)将剩余秒数除以 3600,可以得到小时数 hours = 8805 // 3600 |
| 剩余秒数 = 1605 | 现在的剩余秒数是 r = 8805 % 3600 |
| 分钟数 = 26 | 如果您使用整数除法运算符(//)将剩余秒数除以 60,可以得到分钟数 minutes = 1605 // 60 |
| 秒数 = 45 | 最后的余数,恰好是剩余的秒数,是 seconds = 1605 % 60 |
此算法的 Python 程序如下。
file_13.1-3a
number = int(input("Enter a period of time in seconds: "))
days, r = divmod(number, 86400) # 60 * 60 * 24 = 86400
hours, r = divmod(r, 3600) # 60 * 60 = 3600
minutes, seconds = divmod(r, 60)
print(days, "day(s)", hours, "hour(s)")
print(minutes, "minute(s) and", seconds, "second(s)")
您也可以使用之前练习中的第二种方法来解决此题。您只需首先除以 60,然后再次除以 60,最后除以 24,如下所示。
file_13.1-3b
number = int(input("Enter a period of time in seconds: "))
r, seconds = divmod(number, 60)
r, minutes = divmod(r, 60)
days, hours = divmod(r, 24)
print(days, "day(s)", hours, "hour(s)")
print(minutes, "minute(s) and", seconds, "second(s)")
练习 13.1-4 反转一个数字
编写一个 Python 程序,提示用户输入一个三位整数,然后构建并显示其反转。例如,如果用户输入数字 875,则程序必须显示 578。
解答
要单独计算出用户提供的整数的三个数字,您可以使用第一种或第二种方法。之后,此练习中的唯一困难是构建反转的数字。
以数字 875 为例。隔离后的三位数字将是:
digit1 = 8
digit2 = 7
digit3 = 5
然后你可以通过简单地计算乘积的和来构建反转的数字:
digit3 × 100 + digit2 × 10 + digit1 × 1 = 5 × 100 + 7 × 10 + 8 × 1 = 578
为了改变一下,让我们使用第二种方法来分割用户提供的数字。Python 程序将如下所示。
file_13.1-4
number = int(input("请输入一个三位整数:"))
digit3 = number % 10 # 这是右边的数字
r = number // 10
digit2 = r % 10 # 这是中间的数字
digit1 = r // 10 # 这是左边的数字
reversedNumber = digit3 * 100 + digit2 * 10 + digit1
print(reversedNumber)
13.2 复习练习
完成以下练习。
1)编写一个 Python 程序,提示用户输入任何整数,然后将其最后一位数字乘以 8 并显示结果。
提示:没有必要知道确切的数字位数。你可以使用取模 10 操作来隔离任何整数的最后一位数字。
2)编写一个 Python 程序,提示用户输入一个五位数整数。程序必须然后找到并显示原始数字和其反转的和。例如,如果用户输入数字 32675,程序必须显示消息“32675 + 57623 = 90298”。
3)编写一个 Python 程序,提示用户输入一个整数,然后当数字是奇数时显示 1;否则显示 0。尽量不使用任何决策控制结构,因为你还没有学习过它们!
4)编写一个 Python 程序,提示用户输入一个整数,然后当数字是偶数时显示 1;否则显示 0。尽量不使用任何决策控制结构,因为你还没有学习过它们!
5)编写一个 Python 程序,提示用户输入表示已过时间的秒数的整数,然后以“WW 周(s)DD 天(s)HH 小时(s)MM 分钟(s)和 SS 秒(s)”的格式显示它。例如,如果用户输入数字 2000000,必须显示消息“3 周(s)2 天(s)3 小时(s)33 分钟(s)和 20 秒(s)”。
6)在自动柜员机(ATM)银行机器中,有$20、$10、$5 和$1 的纸币。编写一个 Python 程序,提示用户输入他们想要取出的金额(使用整数值),然后显示 ATM 必须给出的最少纸币数量。例如,如果用户输入$76,程序必须显示消息“3 张$20 的纸币,1 张$10 的纸币,1 张$5 的纸币和 1 张$1 的纸币”。
7)一个机器人到达月球以执行一些实验。机器人的每一步长为 25 英寸。编写一个 Python 程序,提示用户输入机器人迈出的步数,然后计算并显示机器人行走的距离(以英里、英尺、码和英寸为单位)。例如,如果行走的距离是 100000 英寸,程序必须显示消息“1 英里(s),1017 码(s),2 英尺/feet,和 4 英寸(es)”。
已知
►1 英里 = 63360 英寸
►1 码 = 36 英寸
►1 英尺 = 12 英寸
第十四章
操作字符串
14.1 简介
通常来说,字符串是你可以用键盘输入的任何内容,包括字母、符号(如 &、* 和 @)和数字。在 Python 中,字符串总是用单引号或双引号括起来。
下面是一个使用字符串的 Python 程序。
a = "双引号括起来的任何内容都是字符串,即使是数字:"
b = "3, 54, 731"
print(a, b)
print("你甚至可以像这样混合字母、符号和数字:")
print("3 + 4 等于 7")
许多程序处理的数据都是以字符串(文本)的形式出现的。字符串无处不在——从文字处理器到网页浏览器,再到短信程序。本书中的许多练习实际上大量使用了字符串。尽管 Python 支持许多用于操作字符串的有用函数和方法,但本章仅涵盖本书目的所需的那些函数和方法。然而,如果你需要更多信息,你可以访问以下地址之一:
docs.python.org/3.13/library/stdtypes.html#string-methods
.
当需要操作字符串时,可以使用 Python 字符串函数和方法,例如,从字符串中隔离一些字符,移除可能存在于其开头处的空格,或将所有字符转换为大写。
函数和方法不过是解决小问题的小程序。
14.2 字符串中字符的位置
让我们在下面的例子中使用文本“Hello World”。这个字符串由 11 个字符组成(包括两个单词之间的空格字符)。Python 假设第一个字符位于位置 0,第二个字符位于位置 1,以此类推。每个字符的位置如下所示。

空格和其他字符一样,是一个字符。只是因为没有人能看到它,并不意味着它不存在!
14.3 有用的字符串函数/方法(子程序)以及更多
修剪
修剪是从字符串的开始或结束处移除空白字符的过程。
在修剪过程中移除的一些空白字符包括:
►一个普通空格
►一个制表符
►一个新行(换行符)
►一个回车符
例如,你可以修剪用户错误地输入在字符串开头或结尾的任何空格。
你可以使用以下方法来修剪字符串
subject.strip()
此方法返回一个副本,其中从主题字符串的开始和结束处移除了任何空白字符。
示例
file_14.3a
a = " Hello "
b = a.strip()
print(b, "Poseidon!") #它显示:Hello Poseidon!
print(a, "Poseidon!") #它显示: Hello Poseidon!
注意,变量 a 的内容未更改。如果您确实需要更改其内容,可以使用语句 a = a.strip()
字符串替换
subject.replace(search, replace)
此方法在 subject 中搜索并返回一个副本,其中所有搜索字符串的实例都被替换字符串替换。
示例
file_14.3b
a = "I am newbie in Java. Java rocks!"
b = a.replace("Java", "Python")
print(b) #它显示:I am newbie in Python. Python rocks
print(a) #它显示:I am newbie in Java. Java rocks
注意,变量 a 的内容未更改。如果您确实需要更改其内容,可以使用语句 a = a.replace("Java", "Python")
计算字符数
len(subject)
此函数返回 subject 的长度,换句话说,subject 由多少个字符组成。
示例
file_14.3c
a = "Hello Olympians!"
print(len(a)) #它显示:16
b = "I am newbie in Python"
k = len(b)
print(k) #它显示:21
一个字符包括您可以使用键盘输入的任何内容,例如字母、符号、数字和空格字符。
查找字符串位置
subject.find(search)
此方法返回搜索在 subject 中的首次出现的数值位置,如果未找到搜索项,则返回-1。
示例
file_14.3d
a = "I am newbie in Python. Python rocks!"
i = a.find("newbie")
print(i) #它显示:5
print(a.find("Python")) #它显示:15
print(a.find("Java")) #它显示:-1
第一个字符位于位置 0。
转换为小写
subject.lower()
此方法返回一个副本,其中字符串 subject 的所有字母都被转换为小写。
示例
file_14.3e
a = "My NaMe is JohN"
b = a.lower()
print(b) #它显示:my name is john
print(a) #它显示:My NaMe is JohN
注意,变量 a 的内容未更改。如果您确实需要更改其内容,可以使用语句 a = a.lower()
转换为大写
subject.upper()
此方法返回一个副本,其中字符串 subject 的所有字母都被转换为大写。
示例
file_14.3f
a = "My NaMe is JohN"
b = a.upper()
print(b) #它显示:MY NAME IS JOHN
print(a) #它显示:My NaMe is JohN
注意,变量 a 的内容未更改。如果您确实需要更改其内容,可以使用语句 a = a.upper()
示例
file_14.3g
a = "I am newbie in Java. Java rocks!"
b = a.replace("Java", "Python").upper()
print(b) #它显示:I AM NEWBIE IN PYTHON. PYTHON ROCKS
注意方法 replace() 是链接到方法 upper() 的。第一个方法的结果被用作第二个方法的主题。这种写作风格是大多数程序员更喜欢遵循的,因为它有助于节省大量的代码行。当然,你可以链接任意多的方法,但如果链接太多,没有人会理解你的代码。
从字符串中检索单个字符
subject[index]
这返回位于 subject 指定索引处的字符。如前所述,字符串索引从 0 开始。你可以使用索引 0 来访问第一个字符,索引 1 来访问第二个字符,依此类推。最后一个字符的索引是字符串长度减 1。
表示法 subject[index] 被称为“子串表示法”。子串表示法允许你引用字符串中的单个字符。
示例
file_14.3h
a = "Hello World"
print(a[0]) #它显示第一个字母:H
print(a[6]) #它显示:W
print(a[10]) #它显示:d
注意,“Hello”和“World”之间的空格也被视为一个字符。因此,字母 W 存在于位置 6,而不是位置 5。
如果你尝试使用无效的索引,例如大于字符串长度的索引,Python 会抛出一个错误信息,如图 14–1 所示。

图 14–1 表示无效索引的错误信息
字符串索引必须在 0 到字符串长度减 1 的范围内。
如果你想从字符串的末尾开始计数(而不是从开头),你可以使用负索引。例如,索引为 −1 指的是最右边的字符。
在文本“Hello World”中,每个字符的位置(使用负索引)在此显示。

示例
file_14.3i
a = "Hello World"
print(a[-1]) #它显示最后一个字母:d
print(a[-3]) #它显示:r
print(a[-11]) #它显示:H
在 Python 中从字符串中提取单个字符的另一种方法是解包到单独的变量中。
示例
file_14.3j
name = "Zeus"
a, b, c, d = name
print(a) #它显示:Z
print(b) #它显示:e
print(c) #它显示:u
print(d) #它显示:s
这种最后的方法要求你事先知道字符串中有多少个字符。如果你提供的变量数量与字符串中的字符数量不匹配,Python 会抛出一个错误。
获取字符串的一部分(切片表示法)
subject[[beginIndex] : [endIndex] [: step]]
这种表示法返回主题的一部分。具体来说,它返回从位置 beginIndex 开始,直到但不包括 endIndex 的子字符串,或者直到主题的末尾,以先到者为准。两个参数 beginIndex 和 endIndex 都是可选的。如果省略 beginIndex,则返回从位置 0 开始,直到但不包括 endIndex 的子字符串。如果省略 endIndex,则返回从位置 beginIndex 开始直到主题末尾的子字符串。
最后一个参数 step 也是可选的。如果省略,则默认值为 1。如果提供,则定义每次从原始字符串中检索字符后要前进的字符数。
"Python 中的“切片”是一种选择序列(此处为字符串)中一系列元素(此处为字符)的机制。
示例
file_14.3k
a = "Hello World"
print(a[7:9]) #它显示:or
print(a[7:900]) #它显示:orld
print(a[4:10:2]) #步长设置为 2。它显示:oWr
print(a[7:]) #它显示:orld
print(a[:3]) #它显示:Hel
如果你想要从字符串的末尾开始计数(而不是从开头),请使用负索引。
示例
file_14.3l
a = "Hello World"
print(a[-4:-2]) #它显示:or
print(a[-3:]) #它显示:rld
print(a[:-3]) #它显示:Hello Wo
print(a[3:-2]) #它显示:lo Wor
将数字转换为字符串
str(number)
这个函数返回数字的字符串版本,换句话说,它将一个数字(实数或整数)转换为字符串。
示例
file_14.3m
age = int(input("请输入你的年龄:"))
newAge = age + 10
message = "You 'll be " + str(newAge) + " years old in 10 years from now!"
print(message)
练习 14.3-1 显示字符串的反向
编写一个 Python 程序,提示用户输入任何四个字母的字符串,然后显示其反向内容。例如,如果输入的字符串是“Zeus”,则程序必须显示“sueZ”。
解决方案
下面将介绍三种方法。
第一种方法
假设用户的输入被分配给变量 s。你可以使用 s[3] 访问第四个字符,使用 s[2] 访问第三个字符,以此类推。下面的 Python 程序展示了这一点。
file_14.3-1a
s = input("请输入一个四个字母的单词:")
sReversed = s[3] + s[2] + s[1] + s[0]
print(sReversed)
第二种方法
此方法将四个字母解包到四个单独的变量中,如下所示。
file_14.3-1b
s = input("请输入一个四个字母的单词:")
letter1, letter2, letter3, letter4 = s
sReversed = letter4 + letter3 + letter2 + letter1
print(sReversed)
第三种方法
此方法使用切片表示法和 step 参数的负值 -1。
file_14.3-1c
s = input("请输入一个四个字母的单词:")
sReversed = s[::-1]
print(sReversed)
这种方法的优点是用户可以输入任何字符串,无论多短或多长!
练习 14.3-2:交换名字的顺序
编写一个 Python 程序,提示用户输入一个字符串,包含他们的名字和姓氏。最后,程序必须改变两个名字的顺序。
解决方案
这个练习与你在练习 8.1-3 和 8.1-4 中学到的练习不同,后者交换了两个变量的数值。在这个练习中,名字和姓氏都输入在一个单独的字符串中,因此程序必须首先分割字符串,并将每个名字分配给不同的变量。如果你能这样做,然后你就可以以不同的顺序重新连接它们。
让我们通过一个例子来尝试理解这个练习。必须分割的字符串及其各个字符的位置在这里显示。

将第一个名字和最后一个名字在视觉上分开的字符是它们之间的空格字符。问题是这个字符并不总是位于位置 3。有人可能有像“Tom”这样的短名字,而另一个人可能有像“Robert”这样的长名字。因此,你需要一种能够找到空格字符位置的方法,而不管字符串的内容如何。
方法 find()正是你所需要的!如果你用它来查找字符串“Tom Smith”中空格字符的位置,它返回值 3。但如果你用它来查找另一个字符串中的空格字符,比如“Angelina Brown”,它返回的值是 8。
数字 3 不仅仅表示空格字符所在的位置。它还代表单词“Tom”包含的字符数!同样的情况也适用于返回字符串“Angelina Brown”的值 8。它既表示空格字符所在的位置,也代表单词“Angelina”包含的字符数!
该算法的 Python 程序如下所示。
file_14.3-2
fullName = input("Enter your full name: ")
找到空格字符的位置。这也是第一个名字包含的字符数
第一个名字包含的字符数
spacePos = fullName.find(" ")
从 0 位置开始获取 spacePos 个字符
name1 = fullName[:spacePos]
从 spacePos + 1 位置开始获取剩余的字符
name2 = fullName[spacePos + 1:]
fullName = name2 + " " + name1
print(fullName)
表示法 subject[beginIndex : endIndex]返回 subject 的一部分。具体来说,它返回从 beginIndex 位置开始的子串,直到但不包括 endIndex 位置。
请注意,这个程序不能应用于像“Maria Teresa García Ramírez de Arroyo”这样的西班牙名字。原因很明显!
练习 14.3-3:创建登录 ID
编写一个 Python 程序,提示用户输入他们的姓氏,然后从名字的前四个字母(小写)和一个三位随机整数创建一个登录 ID。
解决方案
要创建一个随机整数,可以使用 randrange() 函数。由于你需要一个三位数的随机整数,范围必须在 100 到 999 之间。
该算法的 Python 程序如下所示。
file_14.3-3
from random import randrange
lastName = input("Enter last name: ")
获取 100 到 999 之间的随机整数
randomInt = randrange(100, 1000)
print(lastName[:4].lower() + str(randomInt))
注意,表示法 lastName[:4] 返回从位置 0 开始,到但不包括位置 4 的子串。
练习 14.3-4 创建随机单词
编写一个 Python 程序,显示一个由三个字母组成的随机单词。
解决方案
要创建一个随机单词,你需要一个包含所有 26 个英文字母的变量。然后你可以使用 randrange() 函数来选择一个介于位置 0 和 25 之间的随机字母。
该算法的 Python 程序如下所示。
file_14.3-4a
from random import randrange
alphabet = "abcdefghijklmnopqrstuvwxyz"
randomWord = alphabet[randrange(26)] + alphabet[randrange(26)] + alphabet[randrange(26)]
print(randomWord)
注意,randrange(26) 函数被调用了三次,每次都可能返回一个介于 0 和 25 之间的不同随机整数。
你也可以使用 len() 函数来获取变量 alphabet 的长度,如下所示。
file_14.3-4b
from random import randrange
alphabet = "abcdefghijklmnopqrstuvwxyz"
randomWord = alphabet[randrange(len(alphabet))] + \
alphabet[randrange(len(alphabet))] + \
alphabet[randrange(len(alphabet))]
print(randomWord)
你可以在一个子程序中嵌套另一个子程序。注意函数 len() 是嵌套在函数 randrange() 中的。内部(嵌套)函数的结果被用作外部函数的参数。
在 Python 中,你可以使用每个行尾的回车符(\)将长行拆分为多行(除了最后一行)。
练习 14.3-5 求数字之和
编写一个 Python 程序,提示用户输入一个三位整数,然后计算其数字之和。在不使用整数商(//)和整数余数(%)运算符的情况下解决这个练习。
解决方案
现在你可能会想知道为什么这个练习被放在这个章节,这个章节主要关注字符串操作。你可能会争辩说,你已经知道如何将三位整数拆分为其三个数字,并将每个数字分配给一个单独的变量,就像你在第十三章学习的方法一样,使用了整数商(//)和整数余数(%)运算符。那么,为什么这个练习又在这里讨论呢?
原因是 Python 是一种非常强大的语言,你可以使用它的魔法力量以完全不同的方式解决这个练习。主要思想是将用户提供的整数转换为字符串类型。
第一种方法
在这种方法中,每个数字(每个字符)被分配给单独的变量。
file_14.3-5a
number = int(input("输入一个三位整数:"))
sNumber = str(number)
digit1 = sNumber[0]
digit2 = sNumber[1]
digit3 = sNumber[2]
total = int(digit1) + int(digit2) + int(digit3)
print(total)
由于变量 digit1、digit2 和 digit3 是字符串类型,在将它们的和找到变量 total 中之前,需要使用 int() 函数将它们转换为整数。
第二种方法
在这种方法中,每个数字(每个字符)都被解包到单独的变量中。
file_14.3-5b
number = int(input("输入一个三位整数:"))
digit1, digit2, digit3 = str(number)
total = int(digit1) + int(digit2) + int(digit3)
print(total)
14.4 复习问题:真/假
对以下每个陈述选择真或假。
-
任何你可以用键盘输入的东西都是字符串。
-
字符串必须用括号括起来。
-
短语“Hi there!”包含 8 个字符。
-
在短语“Hi there!”中,字母“t”位于位置 3。
-
语句
y = a[1]将变量 a 中包含的字符串的第二个字符赋给变量 y。 -
以下代码片段满足确定性的属性。
a = "Hello"
y = a[5]
-
去除空白是移除字符串开头或结尾的空白字符的过程。
-
语句
y = "Hello Aphrodite".strip()将值“HelloAphrodite”赋给变量 y。 -
语句
print("Hi there!".replace("Hi", "Hello"))显示的消息是“Hello there!”。 -
以下代码片段将值 4 赋予变量 index。
a = "Hi there"
index = a.find("the")
-
语句
print("hello there!".upper())显示的消息是“Hello There!”。 -
以下代码片段显示消息“Hello there!”。
a = "Hello there!"
print(a[:])
-
语句
print(a[:len(a)])显示变量 a 的某些字母。 -
语句
print(a)等价于语句print(a[:len(a):])。 -
以下代码片段显示单词“HELLO”。
y = "hello there!"
print(y[:5].upper())
-
语句
print(a[len(a) - 1])等价于语句print(a[−1])。 -
以下代码片段显示值 23。
a = 2023
print(str(a)[2:4])
14.5 复习问题:多项选择题
对以下每个陈述选择正确答案。
- 以下哪项不是字符串?
a)“Hello there!”
b)“13”
c)“13.5”
d) 以上所有都是字符串。
- 在字符串“Hello Zeus!”中,空格字符存在于哪个位置?
a)6
b)5
c)空格不是一个字符。
d)以上都不是
- 语句
print(a[len(a) − 2])显示
a)变量 a 的最后一个字符。
b) 变量 a 的倒数第二个字符。
c) 该语句无效。
- 以下语句
y = a.strip().replace("a", "b").replace("w", "y")
等价于以下语句
a) y = a.replace("a", "b").replace("w", "y").strip()
b) y = a.replace("a", "b").strip().replace("w", "y")
c) y = a.strip().replace("w", "y").replace("a", "b")
d) 以上都是
- 语句 a.replace(" ", "")
a) 在变量 a 中的每个字母之间添加一个空格。
b) 从变量 a 中删除所有空格字符。
c) 清空变量 a。
- 语句 "Hello ".replace(" ", "") 等价于以下语句
a) "Hello ".replace("", " ")
b) "Hello ".strip()
c) 以上都是
d) 以上都不是
- 以下代码片段
a = ""
print(len(a))
显示
a) 什么也不做。
b) 1.
c) 0.
d) 该语句无效。
e) 以上都不是
- 以下代码片段将哪个值赋给变量 Shakespeare?
toBeOrNotToBe = "2b Or Not 2b"
Shakespeare = toBeOrNotToBe.find("b")
a) 1
b) 2
c) 11
d) 以上都不是
- 以下代码片段做什么?
a = "Hi there"
b = a[a.find(" ") + 1:]
a) 它将单词“Hi”赋给变量 b。
b) 它将一个空格字符赋给变量 b。
c) 它将单词“there”赋给变量 b。
d) 以上都不是
- 以下代码片段
a = 15
b = 5
print(str(a) + str(b))
显示
a) 155.
b) 10.
c) 15 + 5
d) 以上都不是
14.6 复习练习
完成以下练习。
-
编写一个 Python 程序,创建并显示一个由五个字母组成的随机单词。第一个字母必须是大写字母。
-
编写一个 Python 程序,提示用户输入他们的名字,然后创建一个由从他们的名字中随机挑选的三个小写字母和一个随机四位数组成的秘密密码。例如,如果用户输入“Vassilis Bouras”,一个秘密密码可能是“sar1359”或“vbs7281”或“bor1459”。秘密密码中不允许有空格字符。
-
编写一个 Python 程序,提示用户输入一个三位整数,然后将其反转。例如,如果用户输入数字 375,则必须显示数字 573。解决此练习时,不得使用整数商(//)和整数余数(%)运算符。
-
编写一个 Python 程序,提示用户输入他们的名字、中间名和姓氏,并以以下所有方式显示格式化后的结果。
首字母 MiddleName 姓氏
首字母 M. 姓氏 (其中 M 是中间名首字母)
姓氏 F. (其中 F 是第一个名字的首字母)
此外,程序必须确保无论用户如何输入他们的名字,它都会始终以首字母大写,其余字母小写的方式显示。
例如,假设用户输入以下内容:
名字:Aphrodite
中间名:MARIA
姓氏:boura
程序必须以以下方式显示用户的名字格式:
Aphrodite Maria Boura
Aphrodite M. Boura
Boura A.
5)一些词如“革命性的”和“国际化”的长度非常长,反复书写它们会变得相当累人。在这种情况下,这些词可以用一个特殊的缩写来代替,其制作方法如下:你保留一个词的首字母和尾字母,并在它们之间插入字母的数量。例如,“革命性的”变为“r11y”,“国际化”变为“i18n”。
编写一个 Python 程序,让用户输入一个长词并显示其缩写。
第二十章:在“序列控制结构”中复习
复习填字游戏
1)解决以下填字游戏。

横向
2)len()函数返回字符串中的 _______ 数量。
6)你可以使用键盘输入的任何东西。
7)Python 提供了许多现成的 _________。
8)这种控制结构指的是按行执行,语句按顺序执行。
纵向
1)一个空白字符。
3)从字符串的开始或结束处删除空白字符的过程。
4)sin()函数返回一个数的 _______。
5)abs()函数返回一个数的 _________ 值。
复习问题
回答以下问题。
1)什么是序列控制结构?
2)序列控制结构可以执行哪些操作?
3)给出一些使用整数除法的商和余数的例子。
4)什么是函数或方法?
5)“链式调用方法”这个术语是什么意思?
6)“嵌套函数”这个术语是什么意思?
7)Python 中的“切片”是什么意思?
第四部分
决策控制结构
第十五章
提出问题
15.1 简介
到目前为止,你已经学到了顺序控制结构,其中语句是按顺序执行的,与它们在程序中出现的顺序相同。然而,在严肃的 Python 编程中,很少希望语句按顺序执行。很多时候,你希望在一情况下执行一组语句,而在另一情况下执行完全不同的语句块。
15.2 什么是布尔表达式?
假设变量 x 包含值为 5。这意味着如果你问“x 是否大于 2?”答案显然是“是”。对于计算机来说,这些问题被称为布尔表达式。例如,如果你写 x > 2,这是一个布尔表达式,计算机必须检查表达式 x > 2 是否为 True 或 False。
布尔表达式是一个结果为布尔值(即 True 或 False)的表达式。
布尔表达式是问题,应该读作“某物是否等于/大于/小于另一物?”答案只是一个“是”或“否”(True 或 False)。
决策控制结构可以评估布尔表达式或一组布尔表达式,然后决定执行哪个语句块。
15.3 如何编写简单的布尔表达式
一个简单的布尔表达式写法如下
操作数 1 比较运算符 操作数 2
其中
►操作数 1 和操作数 2 可以是值、变量或数学表达式
►比较运算符可以是表 15-1 中显示的其中之一。
| 比较运算符 | 描述 |
|---|---|
| == | 等于(非赋值) |
| != | 不等于 |
| > | 大于 |
| < | 小于 |
| >= | 大于或等于 |
| <= | 小于或等于 |
表 15-1 Python 中的比较运算符
这里有一些布尔表达式的例子:
►x > y。这个布尔表达式是对计算机的一个问题,可以读作“x 是否大于 y?”
►x <= y。这个布尔表达式也是对计算机的一个问题,可以读作“x 是否小于或等于 y?”
►x != 3 * y + 4。这可以读作“x 是否不等于表达式 3 * y + 4 的结果?”
►s == "Hello"。这可以读作“s 是否等于单词'Hello'?”换句话说,这个问题可以读作“s 是否包含单词'Hello'?”
►x == 5。这可以读作“x 是否等于 5?”
新手程序员在编写 Python 程序时经常犯的一个非常常见的错误是将值赋值运算符与等于运算符混淆。他们经常犯的错误是在实际上想表达 x == 5 时写成 x = 5。
练习 15.3-1 填写表格
根据变量 a、b 和 c 的值,用“True”或“False”填写以下表格。
| a | b | c | a == 10 | b <= a | c > 3 * a − b |
|---|---|---|---|---|---|
| 3 | −5 | 7 | |||
| 10 | 10 | 21 | |||
| −4 | −2 | −9 |
解答
前两个布尔表达式很简单,不需要进一步解释。
关于布尔表达式 c > 3 * a − b,请注意 b 为负数的情况。例如,在第一行中,a 等于 3,b 等于−5。表达式 3 * a - b 的结果是 3 * 3 − (−5) = 3 * 3 + 5 = 14。由于变量 c(在第一行中的)的内容不大于 14,布尔表达式 c > 3 * a − b 的结果是 False。
经过一点工作,表格变为
| a | b | c | a == 10 | b <= a | c > 3 * a − b |
|---|---|---|---|---|---|
| 3 | −5 | 7 | False | True | False |
| 10 | 10 | 21 | True | True | True |
| −4 | −2 | −9 | False | False | True |
15.4 逻辑运算符和复杂布尔表达式
一个复杂布尔表达式可以由更简单的布尔表达式组成,可以写成
BE1 Logical_Operator BE2
where
►BE1 和 BE2 可以是任何布尔表达式。
►逻辑运算符可以是表 15-2 中显示的其中之一。
| 逻辑运算符 | 描述 |
|---|---|
| and | 也称为逻辑合取 |
| or | 也称为逻辑析取 |
| not | 也称为否定或逻辑补码 |
表 15-2 Python 中的逻辑运算符
当你将简单的布尔表达式与逻辑运算符结合时,整个布尔表达式称为“复杂布尔表达式”。例如,表达式 x == 3 and y > 5 是一个复杂布尔表达式。
and 运算符
当你在两个布尔表达式(BE1 和 BE2)之间使用 and 运算符时,这意味着整个复杂布尔表达式的结果是 True,只有当两个布尔表达式(BE1 和 BE2)都是 True 时。
你可以将这些信息组织成一种称为真值表的东西。真值表显示了两个或多个布尔表达式之间逻辑运算的结果,以及它们所有可能值的组合。and 运算符的真值表如下所示。
| BE1(布尔表达式 1) | BE2(布尔表达式 2) | BE1 and BE2 |
|---|---|---|
| False | False | False |
| False | True | False |
| True | False | False |
| True | True | True |
你还感到困惑吗?你不应该感到困惑!这很简单!让我们看看一个例子。复杂布尔表达式
name == "John" and age > 5
只有当变量 name 包含单词“John”(不带双引号)且变量 age 包含一个大于 5 的值时,结果才是 True。两个布尔表达式都必须为 True。如果至少有一个为 False,例如,变量 age 包含一个值为 3,那么整个复杂布尔表达式就是 False。
or 运算符
当你在两个布尔表达式(BE1 或 BE2)之间使用 or 运算符时,这意味着整个复杂布尔表达式的结果是 True,当第一个(BE1)或第二个(BE2)布尔表达式为 True(至少一个)时。
or 运算符的真值表如下所示。
| BE1(布尔表达式 1) | BE2(布尔表达式 2) | BE1 or BE2 |
|---|---|---|
| False | False | False |
| False | True | True |
| True | False | True |
| True | True | True |
让我们看看一个例子。复杂布尔表达式
name == "John" 或 name == "George"
当变量名包含单词“John”或单词“George”(不带引号)时为真。至少有一个布尔表达式必须为真。如果两个布尔表达式都为假,例如,变量名包含单词“Maria”,那么整个复杂布尔表达式为假。
非运算符
当你在布尔表达式(BE)前使用非运算符(not BE)时,意味着当布尔表达式 BE 为假时,整个复杂布尔表达式的结果是真,反之亦然。
非运算符的真值表如下所示。
| BE (布尔表达式) | not BE |
|---|---|
| False | True |
| True | False |
例如,复杂布尔表达式
not age > 5
当变量 age 包含的值小于或等于 5 时为真。例如,如果变量 age 包含的值为 6,那么整个复杂布尔表达式为假。
逻辑运算符非(not)会反转布尔表达式的结果。
练习 15.4-1 计算复杂布尔表达式的结果
当变量 a、b、c 和 d 分别包含值 5、2、7 和 -3 时,计算以下复杂布尔表达式的结果。
i)(3 * a + b / 47 - c * b / a > 23) 且 (b != 2)
ii)(a * b - c / 2 + 21 * c / 3) 或 (a >= 5)
解答
别害怕!结果很容易找到。你只需要回忆一下与和或运算符相关的规则。
i)当两个布尔表达式都为真时,与运算符的结果为真。如果你仔细观察,右边的布尔表达式(b != 2)的结果是假的。所以,你不必浪费时间计算左边布尔表达式的结果。最终结果一定是假的。
ii)当至少有一个布尔表达式为真时,或运算符的结果为真。如果你仔细观察,右边的布尔表达式(a >= 5)的结果实际上是真。所以,不必计算左边布尔表达式的结果。最终结果一定是真的。
15.5 Python 的成员运算符
在 Python 中,成员运算符用于判断操作数是否存在于指定的序列中。有两个成员运算符,如表 15-3 所示。
| 成员运算符 | 描述 |
|---|---|
| in | 如果在指定的序列中找到值,则返回真;否则返回假。 |
| not in | 如果在指定的序列中找不到值,则返回真;否则返回假。 |
表 15-3 Python 中的成员运算符
接下来是一些使用成员运算符的布尔表达式的例子。
►x 在 [3, 5, 9] 范围内。这可以读作“x 是否等于 3,或者等于 5,或者等于 9?”
它也可以写成
x == 3 或 x == 5 或 x == 9
►3 在 [x, y, z] 范围内。这可以读作“3 是否等于 x,或者等于 y,或者等于 z?”
它也可以写成
3 == x 或 3 == y 或 3 == z
►s 在 "ace" 中。这可以读作 '变量 s 的内容是否出现在单词 “ace” 中',或者换句话说,'s 是否等于字母 “a”,或等于字母 “c”,或等于字母 “e”,或等于单词 “ac”,或等于单词 “ce”,或等于单词 “ace”?'
它可以写成等价的形式
s == "a" or s == "c" or s == "e" or s == "ac" or s == "ce" or s == "ace"
布尔表达式 s 在 "ace" 中检查字符串 s 是否可以作为连续字符序列在字符串 "ace" 中找到。换句话说,它将 s 作为 "ace" 中的连续子串来查找。这就是为什么它不会检查子串 "ae";因为字母 “a” 和 “e” 不是连续的。
►s 在 ["a", "c", "e"] 中。这可以读作 's 是否等于字母 “a”,或等于字母 “c”,或等于字母 “e”?'
它也可以写成
s == "a" or s == "c" or s == "e"
►s 不在 ["a", "c", "e"] 中。这可以读作 's 是否不等于字母 “a”,也不等于字母 “c”,也不等于字母 “e”?'
它也可以写成
not(s == "a" or s == "c" or s == "e")
或者作为
s != "a" and s != "c" and s != "e"
15.6 将布尔表达式的结果赋给变量
由于布尔表达式实际上返回一个值(True 或 False),这个值可以直接赋给一个变量。例如,以下语句
a = x > y
将 True 或 False 的值赋给布尔变量 a。它可以读作“如果变量 x 的内容大于变量 y 的内容,将 True 赋值给变量 a;否则,将 False 赋值”。下一个示例将在屏幕上显示 True 值。
file_15.6
x, y = 8, 5
a = x > y
print(a)
15.7 逻辑运算符的优先级顺序是什么?
一个更复杂的布尔表达式可能使用多个逻辑运算符,如这里所示的表达式
name == "Peter" or age > 10 and not name == "Maria"
因此,一个合理的问题是“哪种逻辑运算首先执行?”
Python 中的逻辑运算符遵循大多数编程语言中适用的相同优先级规则。优先级顺序是:首先执行逻辑补码(not),然后执行逻辑合取(and),最后执行逻辑析取(or)。
| 高优先级 
低优先级 | 逻辑运算符 |
| 非 |
|---|
| 和 |
| 或 |
表 15-4 逻辑运算符的优先级顺序
你可以始终使用括号来改变默认的优先级。
练习 15.7-1 填写真值表
根据变量 a、b 和 c 的值,在下面的表格中填写“True”或“False”。
| a | b | c | a > 2 or c > b and c > 2 | not(a > 2 or c > b and c > 2) |
|---|---|---|---|---|
| 1 | −5 | 7 | ||
| 10 | 10 | 3 | ||
| −4 | −2 | −9 |
解答
要计算复杂布尔表达式的结果,你可以使用以下图形方法。
对于 a = 1,b = −5,c = 7,最终结果如这里所示为 True。

与操作具有更高的优先级,并且在与操作之前执行。
对于 a = 10, b = 10, c = 3,最终结果为 True,如下所示。

对于 a = −4, b = −2, c = −9,最终结果为 False,如下所示。

表格第五列的值可以非常容易地计算出来,因为其列标题中的布尔表达式几乎与第四列中的相同。唯一的区别是表达式前面的 not 运算符。因此,第五列的值可以通过简单地否定第四列的结果来计算!
最终的真值表如下所示。
| a | b | c | a > 2 或 c > b 且 c > 2 | not(a > 2 或 c > b 且 c > 2) |
|---|---|---|---|---|
| 1 | −5 | 7 | True | False |
| 10 | 10 | 3 | True | False |
| −4 | −2 | −9 | False | True |
练习 15.7-2 将英语句子转换为布尔表达式
一位班主任要求学生根据他们的年龄举手。他想找到那些
i) 9 至 12 岁之间。
ii) 8 岁以下且 11 岁以上。
iii) 8 岁、10 岁和 12 岁。
iv) 6 至 8 岁之间,以及 10 至 12 岁之间。
v) 既不是 10 岁也不是 12 岁。
解决方案
为了编写所需的布尔表达式,使用了一个变量 age。
i) 句子“9 至 12 岁之间”可以如图所示图形化表示。

但是要注意!在数学中以及在 Python 中,可以写成 9 ≤ age ≤ 12 是有效的
9 <= age <= 12
然而,在大多数计算机语言中,这并不是一个有效的布尔表达式。你可以做的是将表达式分成两部分,如下所示
age >= 9 and age <= 12
这个最后的表达式在大多数计算机语言中都是有效的,包括 Python!
为了确认,你可以在“感兴趣区域”内和区域外(你指定的数据范围)测试这个布尔表达式。例如,对于年龄值 7、8、13 和 17,表达式的结果是 False。相反,对于年龄值 9、10、11 和 12,结果是 True。
ii) 句子“8 岁以下且 11 岁以上”可以如图所示图形化表示。

注意与解决方案(i)中看到的两个圆圈不同。这意味着 8 和 11 这两个值不包括在感兴趣的区域内。
注意句子“8 岁以下且 11 岁以上”。这是一个陷阱!不要犯下写
age < 8 and age > 11
地球上没有一个人可以同时 8 岁以下且 11 岁以上!
陷阱在于单词“和”。尝试重新措辞句子,使其变为“孩子们!如果你 8 岁以下或 11 岁以上,请举手”。现在它更好,正确的布尔表达式变为
age < 8 or age > 11
为了确认,您可以在感兴趣区域内部和外部测试这个表达式。例如,对于年龄值为 8、9、10 和 11 的表达式结果是 False。相反,对于年龄值为 6、7、12 和 15 的表达式结果是 True。
在 Python 中,然而,不要犯这样的错误
8 > age > 11
因为,如果你将表达式分成两部分,它等同于
age < 8 and age > 11
这,正如已经提到的,是错误的!
iii)哎呀!在句子“8, 10, 和 12 岁”中,再次出现了“和”这个词!显然,下一个布尔表达式是错误的。
age == 8 and age == 10 and age == 12
如前所述,没有学生同时是 8、10 和 12 岁!再次强调,正确的布尔表达式必须使用或运算符。
age == 8 or age == 10 or age == 12
为了确认,您可以测试这个表达式的几个值。例如,对于年龄值为 7、9、11 和 13 的表达式结果是 False。对于年龄值为 8、10 和 12 的表达式结果是 True。
在 Python 中,这个复杂的布尔表达式也可以写成
age in [8, 10, 12]
iv)句子“在 6 到 8 岁之间,以及在 10 到 12 岁之间”可以如图所示图形化表示。

布尔表达式是
age >= 6 and age <= 8 or age >= 10 and age <= 12
为了确认,对于年龄值为 5、9、13 和 16 的表达式结果是 False。对于年龄值为 6、7、8、10、11 和 12 的表达式结果是 True。
在 Python 中,这个复杂的布尔表达式也可以写成
6 <= age <= 8 or 10 <= age <= 12
v)句子“既不是 10 岁也不是 12 岁”的布尔表达式可以写成
age != 10 and age != 12
或者也可以写成
not(age == 10 or age == 12)
在 Python 中,这个复杂的布尔表达式也可以写成
age not in [10, 12]
当感兴趣区域的箭头指向彼此时,使用逻辑运算符和。否则,当箭头指向相反方向时,使用或。

15.8 算术、比较、成员和逻辑运算符的优先级顺序是什么?
在许多情况下,一个表达式可能包含不同类型的运算符,如这里所示。
a * b + 2 > 21 or not(c == b / 2) and c > 13
在这种情况下,首先执行算术运算,然后执行比较和成员运算,最后执行逻辑运算,如下表所示。
| 高优先级 
低优先级 | 算术运算符 | ** |
| *, /, //, % |
|---|
| +, − |
| 比较和成员运算符 |
| 逻辑运算符 |
| 和 |
| 或 |
表 15-5 算术、比较、成员和逻辑运算符的优先级顺序
15.9 如何否定布尔表达式
否定是将布尔表达式的意义反转的过程。否定布尔表达式有两种方法。
第一种方法
第一种方法是最简单的一种。只需在原始布尔表达式前加上 not 运算符,你的否定布尔表达式就准备好了!例如,如果原始布尔表达式是
x > 5 且 y == 3
否定后的布尔表达式变为
not(x > 5 and y == 3)
注意,整个表达式必须用括号括起来。如果你没有括号就写下这个表达式,那就完全错误了,就像 not x > 5 and y == 3 这样的写法。在这种情况下,not 运算符只会否定第一个布尔表达式,x > 5。
第二种方法
第二种方法稍微复杂一些,但并不难学。你只需要根据以下表格对每个算子进行否定。
| 原始运算符 | 否定运算符 |
|---|---|
| == | != |
| != | == |
| > | <= |
| < | >= |
| <= | > |
| >= | < |
| in | not in |
| not in | in |
| and | or |
| or | and |
| not | not |
注意,not 运算符保持不变。
例如,如果原始布尔表达式是
x > 5 且 y == 3
否定后的布尔表达式变为
x <= 5 或 y != 3
然而,有一个小细节需要注意。如果一个复杂的布尔表达式中同时存在 and 和 or 运算符,那么在否定布尔表达式中使用 or 运算符的表达式必须用括号括起来,以保持初始的优先级顺序。例如,如果原始布尔表达式是
x >= 5 且 x <= 10 或 y == 3
否定后的布尔表达式必须是
(x < 5 或 x > 10) 且 y != 3
如果你忘记将表达式 x < 5 或 x > 10 用括号括起来,因为 and 运算符的优先级高于 or 运算符,所以表达式 x > 10 and y != 3 会被首先评估,这当然是错误的!
练习 15.9-1 否定布尔表达式
使用两种方法否定以下布尔表达式。
i)b != 4
ii)a * 3 + 2 > 0
iii)not(a == 5 and b >= 7)
iv)a == True
v)b > 7 且 not(x > 4)
vi)a == 4 或 b != 2
解答
第一种方法
i)not(b != 4)
ii)not(a * 3 + 2 > 0)
iii)not(not(a == 5 and b >= 7)),或等价的表达式 a == 5 and b >= 7
两次否定结果为肯定。也就是说,连续两个 not 算子会相互抵消。
iv)not(a == True)
v)not(b > 7 and not(x > 4))
vi)not(a == 4 或 b != 2)
第二种方法
i)b == 4
ii)a * 3 + 2 <= 0
注意,算术运算符不会被“否定”。永远不要用减号(−)替换加号(+)等运算符!
iii)not(a != 5 or b < 7)
注意,not 运算符保持不变。
iv)a != True
v)b <= 7 或 not(x <= 4)
vi)a != 4 且 b == 2
15.10 复习问题:判断对错
对以下每个陈述选择正确或错误。
1)布尔表达式是一个总是产生两个值之一的表达式。
2)布尔表达式至少包含一个逻辑运算符。
3)在 Python 中,表达式 x = 5 检查变量 x 是否等于 5。
4)语句 a = b == c 不是一个有效的 Python 语句。
5)布尔表达式 b < 5 检查变量 b 是否小于或等于 5。
6)与运算符也称为逻辑析取运算符。
7)或运算符也称为逻辑补运算符。
8)如果两个布尔表达式都为 True,则两个布尔表达式的逻辑合取等于它们的逻辑析取的结果。
9)如果两个布尔表达式具有不同的值,则两个布尔表达式的逻辑析取的结果一定是 True。
10)表达式 c == 3 and d > 7 被认为是复杂的布尔表达式。
11)当两个操作数(布尔表达式)都为 True 时,逻辑运算符或的结果为 True。
12)当变量 x 包含除 5 以外的任何值时,布尔表达式 not(x == 5) 的结果为 True。
13)非运算符在逻辑运算符中优先级最高。
14)或运算符在逻辑运算符中优先级最低。
15)在布尔表达式 (x > y or x == 5) and x <= z 中,与运算先于或运算执行。
16)在布尔表达式 a * b + c > 21 or c == b / 2 中,程序首先检查 c 是否大于 21。
17)当老师想要找到年龄在 8 岁以下且超过 11 岁的学生时,相应的布尔表达式是 8 > age > 11。
18)对于任何 x 的值,布尔表达式 x < 0 and x > 100 总是 False。
19)对于任何 x 的值,布尔表达式 x > 0 or x < 100 总是 True。
20)布尔表达式 x > 5 等价于 not(x < 5)。
21)布尔表达式 not(x > 5 and y == 5) 不等价于 not(x > 5) and y == 5。
22)在威廉·莎士比亚^([13])的《哈姆雷特》(第三幕,第一场)中,主要角色说“生存,还是毁灭:这是一个问题:……”。如果你将其写成布尔表达式 toBe or not toBe,这个“Shakesboolean”表达式的结果在以下代码片段中为 True。
toBe = 1 > 0
thatIsTheQuestion = toBe or not toBe
23)布尔表达式 not(not(x > 5)) 等价于 x > 5。
15.11 复习问题:多项选择题
选择以下每个陈述的正确答案。
1)以下哪个不是比较运算符?
a)>=
b)=<
c)<
d)所有这些都是比较运算符。
2)以下哪个不是 Python 逻辑运算符?
a)nor
b)not
c)所有这些都是逻辑运算符。
d)以上都不是逻辑运算符。
3)如果变量 x 包含值为 5,那么语句 y = x % 2 == 1 将把什么值赋给变量 y?
a)正确
b)False
c)1
d)以上都不是
4)如果变量 x 包含值为 5,那么语句 y = x % 2 == 0 or int(x / 2.0) == 2 将把什么值赋给变量 y?
a)正确
b)False
c)以上都不是
5)实验室的温度必须在 50 到 80 华氏度之间。以下哪个布尔表达式用于测试这个条件?
a)t >= 50 or t <= 80
b)50 >= t >= 80
c)t >= 50 and t <= 80
d)t > 50 或 t < 80
e)以上皆非
6)以下哪个与布尔表达式 t == 3 或 t > 30 等价?
a)t == 3 且 not(t <= 30)
b)t == 3 and not(t < 30)
c)not(t != 3) 或 not(t < 30)
d)not(t != 3 and t <= 30)
e)以上皆非
15.12 复习练习
完成以下练习。
1)将第一列的每个元素与第二列的一个或多个元素匹配。
| 描述 | 运算符 |
|---|---|
| i) 逻辑运算符 | a) % |
| ii) 算术运算符 | b) += |
| iii) 比较运算符 | c) and |
| ii) 赋值运算符(一般) | d) == |
| e) 或 | |
| f) >= | |
| g) not | |
| h) = | |
| i) *= | |
| j) / |
2)根据变量 a、b 和 c 的值,用“True”或“False”填写以下表格。
| a | b | c | a != 1 | b > a | c / 2 > 2 * a |
|---|---|---|---|---|---|
| 3 | −5 | 8 | |||
| 1 | 10 | 20 | |||
| −4 | −2 | −9 |
3)根据布尔表达式 BE1 和 BE2 的值,用“True”或“False”填写以下表格。
| BE1 (布尔表达式 1) | BE2 (布尔表达式 2) | BE1 或 BE2 | BE1 且 BE2 | 非(BE2) |
|---|---|---|---|---|
| False | False | |||
| False | True | |||
| True | False | |||
| True | True |
4)根据变量 a、b 和 c 的值,用“True”或“False”填写以下表格。
| a | b | c | a > 3 或 c > b 且 c > 1 | a > 3 且 c > b 或 c > 1 |
|---|---|---|---|---|
| 4 | −6 | 2 | ||
| −3 | 2 | −4 | ||
| 2 | 5 | 5 |
5)对于 x = 4,y = −2 和 flag = True,用相应的值填写以下表格。
| 表达式 | 值 |
|---|---|
| (x + y) ** 3 | |
| (x + y) / (x ** 2 − 14) | |
| x − 1 == y + 5 | |
| x > 2 and y == 1 | |
| x == 1 or y == −2 and not(flag == False) | |
| not(x >= 3) and (x % 2 > 1) |
6)当变量 a、b、c 和 d 分别包含值 6、−3、4 和 7 时,计算以下每个复杂布尔表达式的结果。
i)(3 * a + b / 5 - c * b / a > 4) and (b != -3)
ii)(a * b - c / 2 + 21 * c / 3 != 8) 或 (a >= 5)
提示:从表达式的简单部分开始评估。
7)班主任要求学生根据年龄举手。他想找到的是:
i)小于 12 岁,但不是 8 岁的人。
ii)在 6 到 9 岁之间,也是 11 岁的人。
iii)超过 7 岁,但不是 10 或 12 岁的那些人。
iv)6 岁、9 岁和 11 岁。
v)在 6 到 12 岁之间,但不是 8 岁的人。
vi)既不是 7 岁也不是 10 岁。
要编写所需的布尔表达式,请使用名为 age 的变量。
8)在不给表达式前面添加 not 运算符的情况下,否定以下布尔表达式。
i)x == 4 且 y != 3
ii)x + 4 <= 0
iii)not(x > 5) 或 y == 4
iv)x != False
v)not(x >= 4 or z > 4)
vi)x != 2 and x >= −5
- 如您所知,两次否定会产生肯定。通过两次否定(应用两种方法)写出以下布尔表达式的等价形式。
i)x >= 4 and y != 10
ii)x - 2 >= 9
iii)not(x >= 2) or y != 4
iv)x != False or y == 3
v)not(x >= 2 and y >= 2)
vi)x != −2 and x <= 2
第十六章
单一选择决策结构
16.1 单一选择决策结构
这是最简单的决策控制结构。它只包括在“True”路径上的一个语句或语句块,如下面的流程图片段所示,以一般形式给出。

如果布尔表达式 Boolean_Expression 评估为 True,则执行结构中的语句或语句块;否则,跳过语句。
Python 语句的一般形式是
if Boolean_Expression:
语句或语句块
注意,语句或语句块缩进了 4 个空格。
在下一个示例中,当用户输入小于 18 的值时,会显示消息“您未成年!”。当用户输入大于或等于 18 的值时,不会显示任何内容。
file_16.1a
age = int(input("Enter your age: "))
if age < 18:
print("You are underage!")
注意,print()语句缩进了 4 个空格。
在下一个示例中,当用户输入小于 18 的值时,会显示消息“您未成年!”和消息“您需要再等几年”。与之前一样,当用户输入大于或等于 18 的值时,不会显示任何消息。
file_16.1b
age = int(input("Enter your age: "))
if age < 18:
print("You are underage!")
print("You have to wait for a few more years.")
注意,两个 print()语句都缩进了 4 个空格。
Python 是第一个强制执行缩进的编程语言之一。Python 通过缩进来指定多个语句属于一个组。缩进的组称为“语句块”或“代码块”。在 Python 中,缩进是强制性的。属于代码块的代码必须缩进。例如,所有出现在 if 语句内部的语句都必须向右缩进相同数量的空格;否则,它们不被视为 if 语句的一部分,您可能会收到错误消息。关于代码块语法的两个简单规则要记住:
► 代码块的第一行语句总是以冒号(:)字符结尾。
► 第一行下面的代码必须缩进。
Python 的官方网站建议每个缩进级别使用 4 个空格。如果您需要更多信息,可以访问:
www.python.org/dev/peps/pep-0008
.
为了缩进文本光标,您可以通过按一次“Tab
”键来代替输入空格字符!
为了缩进一个现有的语句或一组语句,选择它并按“Tab
”键!
为了取消缩进一个语句或一组语句,选择它并按“Shift ↑ + Tab
”键组合!
在 Python 以外的计算机语言中,如 C、C++、C#、Java 或 Visual Basic,缩进不是强制性的,但它对于使代码更容易阅读是非常必要的。它还有助于程序员更容易地学习和理解他人编写的代码。
在下一个例子中,当用户输入名字“宙斯”时,才会显示消息“你是众神之王!”。然而,无论用户输入什么名字,消息“你住在奥林匹斯山上”总是显示。
file_16.1c
name = input("输入一个奥林匹克运动员的名字:")
if name == "Zeus":
print("你是众神之王!")
print("你住在奥林匹斯山上。")
请注意,最后的 print()语句没有缩进,因此它不属于单分支决策结构的语句块。
新手程序员在编写 Python 程序时,一个常见的错误是将赋值运算符与“等于”运算符混淆。他们经常犯的错误是在实际上想要说 if name == "Zeus"时写成 if name = "Zeus"。
当 if 语句中只使用一个语句时,你可以将其写在一行上,如下所示:
if Boolean_Expression: One_Single_Statement
以下两个例子是等价的。
if x == y: x += 1
print(x)
if x == y:
x += 1
print(x)
练习 16.1-1 跟踪表和单分支决策结构
设计相应的流程图并创建跟踪表,以确定下一个 Python 程序在两次不同执行中的每个步骤中变量的值。
两次执行的输入值分别是(i)10 和(ii)51。
file_16.1-1
a = int(input())
y = 5
if a * 2 > 100:
a = a * 3
y = a * 4
print(a, y)
解答
流程图如下所示。

每个输入的跟踪表如下所示。
i)对于输入值 10,跟踪表如下所示。
| 步骤 | 语句 | 备注 | a | y |
|---|---|---|---|---|
| 1 | a = int(input()) | 用户输入值 10 | 10 | ? |
| 2 | y = 5 | 10 | 5 | |
| 3 | if a * 2 > 100: | 这评估为 False | ||
| 4 | print(a, y) | 显示:10 5 |
ii)对于输入值 51,跟踪表如下所示。
| 步骤 | 语句 | 备注 | a | y |
|---|---|---|---|---|
| 1 | a = int(input()) | 用户输入值 51 | 51 | ? |
| 2 | y = 5 | 51 | 5 | |
| 3 | if a * 2 > 100: | 这评估为 True | ||
| 4 | a = a * 3 | 153 | 5 | |
| 5 | y = a * 4 | 153 | 612 | |
| 6 | print(a, y) | 显示:153 612 |
练习 16.1-2 数字的绝对值
设计一个流程图并编写相应的 Python 程序,允许用户输入一个数字,然后显示其绝对值。
解决方案
实际上,有两种方法。第一种方法使用单分支决策结构,而第二种方法使用内置的 abs() 函数。
第一种方法 - 使用单分支决策结构
该方法很简单。如果用户输入一个负值,例如 -5,则该值会改变并显示为 +5。然而,正数或零保持不变。解决方案如下所示的流程图所示。
.
相应的 Python 程序如下所示。
file_16.1-2a
x = float(input())
if x < 0:
x = (-1) * x
print(x)
第二种方法 - 使用 abs() 函数
在这种情况下,您只需要两行代码,无需任何决策控制结构!
file_16.1-2b
x = float(input())
print(abs(x))
16.2 复习问题:判断题
选择以下每个语句的真或假。
1)当必须执行一系列语句时使用单分支决策结构。
2)您使用单分支决策结构,以便其他程序员更容易理解您的程序。
3)在单分支决策结构中,可能没有任何语句被执行。
4)在流程图中,决策符号代表算法的开始和结束。
5)以下代码在语法上是正确的。
if = 5
x = if + 5
print(x)
6)单分支决策结构使用保留关键字 else。
7)以下代码片段满足确定性的属性。
if b != 3:
x = a / (b - 3)
8)以下 Python 程序满足确定性的属性。
a = float(input())
b = float(input())
if b != 3:
x = a / (b - 3)
print(x)
9)包含决策控制结构且未使用代码缩进的 Python 程序无法由计算机执行。
16.3 复习问题:多项选择题
选择以下每个语句的正确答案。
1)当以下情况发生时使用单分支决策结构
a)语句一个接一个地执行。
b)在执行某些语句之前必须做出决定。
c)以上皆非
d)所有上述选项
2)单分支决策结构包括一个语句或语句块。
a)只有错误路径。
b)两条路径。
c)只有当变量 x 包含除 3 以外的值时。
3)在以下代码片段中,
if x == 3:
x = 5
y += 1
执行语句 y += 1
a)只有当变量 x 包含值为 3 的值时。
b)只有当变量 x 包含值为 5 的值时。
c)只有当变量 x 包含除 3 以外的值时。
d)总是。
4)在以下代码片段中,
if x % 2 == 0: y += 1
当执行语句 y += 1 时
a)变量 x 能被 2 整除。
b)变量 x 包含一个偶数。
c)变量 x 不包含一个奇数。
d)所有上述选项
e)以上皆非
5)在以下代码片段中,
x = 3 * y
if x > y: y += 1
语句 y += 1 是
a)总是执行。
b)从不执行。
c)仅在变量 y 包含正值时执行。
d)以上都不是
6)以下程序
x = int(input())
if x < 0:
x = (-1) * x
print(x)
cannot be executed by a computer because
a)它没有使用代码缩进。
b)它包含逻辑错误。
c)以上都不是
16.4 复习练习
完成以下练习。
1)识别以下 Python 程序中的语法错误:
x = float(input())
y ← - 5
if x * y / 2 > 20
y =* 1
x += 4 * x²
print(x y)
2)创建一个跟踪表,以确定以下 Python 程序在两次不同执行中每一步的变量值。然后,设计相应的流程图。
两次执行输入的值分别是(i)10 和(ii)-10。
x = float(input())
y = -5
if x * y / 2 > 20:
y -= 1
x -= 4
if x > 0:
y += 30
x = x ** 2
print(x, ",", y)
3)创建一个跟踪表,以确定以下 Python 程序在两次不同执行中每一步的变量值。然后,设计相应的流程图。
两次执行输入的值分别是(i)-11 和(ii)11。
x = int(input())
y = 8
if abs(x) > 10:
y += x
x -= 1
if abs(x) > 10:
y *= 3
print(x, ",", y)
4)创建一个跟踪表,以确定以下 Python 程序在两次不同执行中每一步的变量值。然后,设计相应的流程图。
两次执行输入的值分别是(i)1,2,3 和(ii)4,2,1。
x = int(input())
y = int(input())
z = int(input())
if x + y > z: x = y + z
if x > y + z: y = x + z
if x > y - z: z = x - z % 2
print(x, ",", y, ",", z)
5)编写一个 Python 程序,提示用户输入一个数字,然后当用户提供的数字是正数时,显示消息“正数”。
6)编写一个 Python 程序,提示用户输入两个数字,然后当两个用户提供的数字都是正数时,显示消息“两个都是正数”。
7)编写一个 Python 程序,提示用户输入他们的年龄,然后当用户提供的年龄大于 14 岁时,显示消息“您可以在堪萨斯州(美国)开车”。
8)编写一个 Python 程序,提示用户输入一个字符串,然后当用户提供的字符串只包含大写字母时,显示消息“大写”。
提示:使用 upper()方法。
9)编写一个 Python 程序,提示用户输入一个字符串,然后当用户提供的字符串包含超过 20 个字符时,显示消息“许多字符”。
提示:使用 len()函数。
10)编写一个 Python 程序,提示用户输入四个数字,如果其中至少有一个是负数,则显示消息“在提供的数字中,有一个是负数!”
-
编写一个 Python 程序,提示用户输入两个数字。如果第一个用户提供的数字大于第二个数字,程序必须交换它们的值。最后,程序必须以升序显示这两个数字。
-
编写一个 Python 程序,提示用户输入纽约三个不同地点测量的三个温度值,如果平均值大于 60 华氏度,则显示消息“热浪”。
第十七章
双重选择决策结构
17.1 双重选择决策结构
与单选择决策结构相比,这种决策控制结构在两个路径上都包含一个语句或语句块。

如果布尔表达式 Boolean_Expression 评估为 True,则执行语句或语句块 1;否则,执行语句或语句块 2。
Python 语句的一般形式是
if Boolean_Expression:
一个语句或语句块 1
else:
一个语句或语句块 2
在下一个示例中,当用户输入大于或等于 18 的值时,会显示消息“您已成年”。否则,会显示消息“您未成年!”
file_17.1
age = int(input("Enter your age: "))
if age >= 18:
print("You are an adult!")
else:
print("You are underage!")
练习 17.1-1 寻找输出消息
对于以下流程图,确定三种不同执行中的输出消息。
三次执行输入的值分别是:(i) 3,(ii) −3,和 (iii) 0。

解答
i)当用户输入值 3 时,布尔表达式评估为 True。执行流程遵循正确路径,并显示消息“Positive”。
ii)当用户输入值 −3 时,布尔表达式评估为 False。执行流程遵循左侧路径,并显示消息“Negative”。
iii)你能预测当用户输入值 0 时会发生什么吗?如果你认为不会显示任何消息,那你就错了!双重选择决策结构必须始终遵循一个路径,要么是右侧,要么是左侧!它不能跳过执行两个语句块。至少必须执行一个语句或语句块。所以,在这种情况下,当用户输入值 0 时,布尔表达式评估为 False,执行流程遵循左侧路径,并显示消息“Negative”。
该算法目前的形式并不满足对所有可能输入的有效性属性。虽然它正确地识别了正数和负数,但它忽略了零的情况。零是一个有效的输入,算法的逻辑应该考虑到这一点,以确保对所有潜在的输入产生有意义的输出。在本书的后面部分(在练习 19.1-2 中),你将学习如何根据用户提供的值是大于、小于还是等于零来显示三条不同的消息。
决策符号有一个入口和两个出口路径!你不能有第三个出口!
练习 17.1-2 跟踪表和双重选择决策结构
创建一个跟踪表,以确定在下一个 Python 程序的每个步骤中变量的值,针对两种不同的执行。
两次执行的输入值分别是 (i) 5 和 (ii) 10。
file_17.1-2
a = float(input())
z = a * 10
w = (z - 4) * (a - 3) / 7 + 36
if a < z >= w:
y = 2 * a
else:
y = 4 * a
打印(y)
解答
i)对于输入值 5,跟踪表如下。
| 步骤 | 语句 | 备注 | a | z | w | y |
|---|---|---|---|---|---|---|
| 1 | a = float(input()) | 用户输入值 5 | 5.0 | ? | ? | ? |
| 2 | z = a * 10 | 5.0 | 50.0 | ? | ? | |
| 3 | w = (z - 4) * (a - 3) / 7 + 36 | 5.0 | 50.0 | 49.142 | ? | |
| 4 | if a < z >= w: | 这将评估为 True | ||||
| 5 | y = 2 * a | 5.0 | 50.0 | 49.142 | 10.0 | |
| 6 | print(y) | 它显示:10.0 |
ii)对于输入值 10,跟踪表如下。
| 步骤 | 语句 | 备注 | a | z | w | y |
|---|---|---|---|---|---|---|
| 1 | a = float(input()) | 用户输入值 10 | 10.0 | ? | ? | ? |
| 2 | z = a * 10 | 10.0 | 100.0 | ? | ? | |
| 3 | w = (z - 4) * (a - 3) / 7 + 36 | 10.0 | 100.0 | 132.0 | ? | |
| 4 | if a < z >= w: | 这将评估为 False | ||||
| 5 | y = 4 * a | 10.0 | 100.0 | 132.0 | 40.0 | |
| 6 | print(y) | 它显示:40.0 |
练习 17.1-3 谁是最大的?
设计一个流程图并编写相应的 Python 程序,允许用户输入两个数字 A 和 B,然后确定并显示这两个数字中较大的一个。
解答
此练习可以使用双重或单选决策结构来解决。
第一种方法 - 使用双重决策结构
此方法测试数字 B 的值是否大于数字 A 的值。如果是这样,数字 B 是最大的;否则,数字 A 是最大的。使用此方法解决此练习的相应流程图如下所示

并且 Python 程序如下。
file_17.1-3a
a = float(input())
b = float(input())
if b > a:
maximum = b
else:
maximum = a
打印("最大值:", maximum)
请注意,此练习试图确定最大值,而不是这个值实际分配给哪个变量(变量 A 或变量 B)。
第二种方法 - 使用单选决策结构
如下流程图所示,这种方法最初假设数字 A 可能是最大值(这就是为什么它将变量 a 的值赋给变量 maximum)。然而,如果结果证明数字 B 大于数字 A,则最大值将被更新;变量 maximum 将被赋予新的值——变量 b 的值。因此,无论数字 A 和 B 的值如何,最终变量 maximum 都将始终包含最大值!

此处的 Python 程序如下。
file_17.1-3b
a = float(input())
b = float(input())
maximum = a
if b > a:
maximum = b
打印("最大值:", maximum)
练习 17.1-4 查找奇数和偶数
设计一个流程图并编写相应的 Python 程序,提示用户输入一个正整数,然后显示一条消息,指出该数字是偶数;否则,显示“奇数”。
解决方案
接下来你可以找到各种奇数和偶数:
►奇数:1, 3, 5, 7, 9, 11, …
►偶数:0, 2, 4, 6, 8, 10, 12, …。
注意,零被认为是偶数。
在这个练习中,你需要找到一种方法来确定一个数字是奇数还是偶数。你需要找到所有偶数之间的共同属性,或者所有奇数之间的共同属性。实际上有一个!所有偶数都能被 2 整除。所以,当操作 x MOD 2 的结果等于 0 时,x 是偶数;否则,x 是奇数。
流程图如下所示。

并且 Python 程序如下。
file_17.1-4
x = int(input("输入一个正整数:"))
if x % 2 == 0:
打印("偶数")
else:
打印("奇数")
练习 17.1-5 每周工资
总工资取决于工资率和每周工作的小时总数。然而,如果有人工作超过 40 小时,他们将获得超过 40 小时的工作时间工资。设计一个流程图并编写相应的 Python 程序,让用户输入工资率和工作时间,然后计算并显示总工资。
解决方案
这个练习可以使用双重选择决策结构来解决。当工作时间超过 40 小时时,总工资的计算如下:
总工资 = (工资率) × 40 + 1.5 × (工资率) × (超过 40 小时的工作小时数)
解决这个问题的流程图如下所示。

并且 Python 程序如下所示。
file_17.1-5
payRate = float(input())
hoursWorked = int(input())
if hoursWorked <= 40:
grossPay = payRate * hoursWorked
else:
grossPay = payRate * 40 + 1.5 * payRate * (hoursWorked - 40)
打印("总工资:", grossPay)
17.2 复习问题:真/假
对以下每个陈述选择正确或错误。
1)在双重选择决策结构中,可能没有任何陈述被执行。
2)双重选择决策结构必须至少包含两个语句。
3)双重选择决策结构使用保留关键字 else。
4)以下陈述在语法上是正确的。
else = 5
5)在双重选择决策结构中,评估的布尔表达式可以返回超过两个值。
6)以下代码片段满足有效性的属性。
x = int(input())
y = int(input())
z = int(input())
if x > y and x > z:
打印("值", x, "是最大的一个")
else:
打印("值", y, "是最大的一个")
17.3 复习问题:多项选择题
对以下每个陈述选择正确答案。
1)双重选择决策结构包括一个语句或语句块。
a)仅错误路径。
b)两条路径。
c)仅真实路径。
2)在以下代码片段中,
if x % 2 == 0:
x = 0
else:
y += 1
the statement y += 1 is executed when
a)变量 x 正好能被 2 整除。
b)变量 x 包含一个偶数。
c)变量 x 包含一个奇数。
d)以上都不是
3)在以下代码片段中,
if x == 3:
x = 5
else:
x = 7
y += 1
执行语句 y += 1 的条件是
a)当变量 x 包含的值为 3 时。
b)当变量 x 包含的值不是 3 时。
c)以上都是
17.4 复习练习
完成以下练习。
1)为以下 Python 程序的每一步创建一个跟踪表以确定两次不同执行中变量的值。然后,设计相应的流程图。
两次执行的输入值分别是(i)3 和(ii)0.5。
a = float(input())
z = a * 3 - 2
if z >= 1:
y = 6 * a
else:
z += 1
y = 6 * a + z
print(z, ",", y)
2)为以下 Python 程序的每一步创建一个跟踪表以确定变量的值。然后,设计相应的流程图。
from math import sqrt
x = 3
y = x ** 3 + 9
z = 2 * x + y - 4
if x > y:
y = z % x
z = sqrt(x)
else:
x = z % y
z = sqrt(y)
print(x, ",", y, ",", z)
3)编写与以下流程图相对应的 Python 程序,然后创建一个跟踪表以确定两次不同执行中每一步的变量值。
两次执行的输入值分别是(i)10 和(ii)2。

4)使用双重选择结构,编写一个 Python 程序,提示用户输入一个数字,然后显示一条消息,指出用户提供的数字是否大于 100。如果不是,则显示“提供的数字小于或等于 100”。
5)使用双重选择结构,编写一个 Python 程序,提示用户输入一个数字,然后显示一条消息,指出用户提供的数字是否在 0 到 100 之间。如果不是,则显示“提供的数字不在 0 到 100 之间”。
6)两支足球队在欧洲冠军联赛中对战。使用双重选择结构,编写一个 Python 程序,提示用户输入两队的名称和每队得分,然后显示获胜队的名称。假设用户输入的是有效值,并且没有平局(平局)。
7)使用双重选择结构,编写一个 Python 程序,允许用户输入一个整数,然后显示一条消息,指出用户提供的数字是否是 6 的倍数;如果不是,则显示“NN 不是 6 的倍数”(其中 NN 是用户提供的数字)。假设用户输入的是一个非负值^([14])。
-
使用双重选择决策结构,编写一个 Python 程序,允许用户输入一个整数,然后显示两种可能的消息之一。一条消息指示用户提供的数字是否是 6 的倍数或 7 的倍数;另一条消息指示用户提供的数字既不是 6 的倍数也不是 7 的倍数。假设用户输入的是一个非负值。
-
使用双重选择决策结构,编写一个 Python 程序,允许用户输入一个整数。程序必须显示一条消息,指示用户提供的数字是否是 4 的倍数;如果不是,则显示“NN 不是 4 的倍数”(其中 NN 是用户提供的数字)。此外,程序必须以“NN = QQ x 4 + RR”的形式呈现用户提供的整数的结构,其中 QQ 是整数商,RR 是 NN 除以 4 时的整数余数。例如,如果用户提供的整数是 14,则必须显示消息“14 = 3 x 4 + 2”。假设用户输入的是一个非负值。
-
使用双重选择决策结构,设计一个流程图并编写相应的 Python 程序,允许用户输入两个值,然后确定并显示两个值中的较小值。假设用户输入的是两个不同的值。
-
使用双重选择决策结构,编写一个 Python 程序,允许用户输入三个数字,然后显示一条消息,指示用户提供的数字是否可以是三角形三边的长度;如果不是,则显示“提供的数字不能是三角形三边的长度”。假设用户输入的是有效值。
提示:在任何三角形中,每边的长度都小于其他两边长度的和。
- 使用双重选择决策结构,编写一个 Python 程序,允许用户输入三个数字,然后显示一条消息,指示用户提供的数字是否可以是直角三角形(或直角三角形)的三边长度;如果不是,则显示“提供的数字不能是直角三角形的三边长度”。假设用户输入的是有效值。
提示 1:使用勾股定理。
提示 2:您可以使用长度为 3、4 和 5(这些可以是直角三角形的三边长度)来测试您的程序。
- 2004 年雅典奥运会上跳远项目的运动员参加了三次不同的资格赛跳。为了获得资格,运动员必须达到至少 8 米的平均跳远距离。使用双重选择决策结构,编写一个 Python 程序,提示用户输入三次表现,当平均值为 8 米或以上时显示消息“合格”;否则显示“不合格”。假设用户输入的是有效值。
14)总工资取决于工资率和每周工作的小时总数。然而,如果有人工作超过 40 小时,他们将获得超过 40 小时工作时间的双倍工资。使用双重选择决策结构,设计一个流程图并编写相应的 Python 程序,允许用户输入工资率和工作时间,然后计算并显示净工资。净工资是在扣除任何扣除后的实际支付给员工的工资金额。扣除包括税收、健康保险、退休计划等。假设总扣除率为 30%。此外,假设用户输入了有效的值。
15)定期保养可以使您的车辆更加可靠,减少故障、不便和不必要的费用。一般来说,您需要执行两种类型的保养:
a)每行驶 6000 英里进行一次小保养
b)每行驶 12000 英里进行一次大保养
使用双重选择决策结构,编写一个 Python 程序,提示用户输入行驶的英里数,然后计算并显示距离下一次保养还有多少英里,以及下一次保养的类型。假设用户输入了有效的值。
16)两辆车从静止开始,沿直线水平道路以恒定加速度行驶指定的时间。使用双重选择决策结构,编写一个 Python 程序,提示用户输入两辆车行驶的时间(对两辆车都相同)以及每辆车的加速度,然后计算并显示它们之间的距离以及消息“车 A 领先”或“车 B 领先”,取决于哪辆车领先比赛。所需的公式是
.
在哪里
►S 是汽车行驶的距离,以米(m)为单位
►u[o]是汽车的初始速度(速度),以每秒米(m/sec)为单位
►t 是汽车行驶的时间,以秒(sec)为单位
►a 是加速度,以每秒平方米(m/sec²)为单位
假设用户提供的加速度值各不相同。此外,假设用户输入了有效的值。
第十八章
多重条件决策结构
18.1 多重条件决策结构
多重条件决策结构用于扩展选择数量,如下面的流程图片段所示。

当执行多重条件决策结构时,会评估 Boolean_Expression_1。如果评估为真,则执行紧随其后的相应语句或语句块;然后跳过结构的其余部分,继续执行多重条件决策结构之后可能存在的任何剩余语句。然而,如果 Boolean_Expression_1 评估为假,则执行流程会评估 Boolean_Expression_2。如果它评估为真,则执行紧随其后的相应语句或语句块,并跳过结构的其余部分。这个过程会一直继续,直到有一个布尔表达式评估为真,或者直到没有更多的布尔表达式为止。
当之前的布尔表达式都没有评估为真时,执行最后一个语句或最后一个语句块 N + 1。此外,这个最后一个语句或语句块 N + 1 是可选的,并且可以省略。这取决于你试图解决的问题的算法。
Python 语句的一般形式是
if Boolean_Expression_1:
一个或多个语句 1
elif Boolean_Expression_2:
一个或多个语句 2
elif Boolean_Expression_3:
一个或多个语句 3
.
.
.
elif Boolean_Expression_N:
一个或多个语句 N
else:
一个或多个语句 N + 1
elif 关键字是“else if”的缩写。
最后一个语句或最后一个语句块 N + 1 是可选的,并且可以省略(也需要省略 else 关键字)。
这里展示了一个简单的例子。
file_18.1
name = input("你叫什么名字? ")
if name == "John":
print("你是我的堂兄/堂姐!")
elif name == "Aphrodite":
print("你是我的姐姐!")
elif name == "Loukia":
print("你是我的妈妈!")
else:
print("对不起,我不认识你。")
练习 18.1-1 跟踪表和多重条件决策结构
创建一个跟踪表,以确定在 Python 程序的下述三次执行中,每个步骤中变量的值。
三次执行的输入值分别为:(i) 5, 8;(ii) −13, 0;(iii) 1, −1。
file_18.1-1
a = int(input())
b = int(input())
if a > 3:
print("消息 #1")
elif a > 4 and b <= 10:
print("消息 #2")
print("消息 #3")
elif a * 2 == -26:
print("消息 #4")
print("消息 #5")
b += 1
elif b == 1:
print("消息 #6")
else:
print("消息 #7")
print("消息 #8")
print("结束!")
解答
i)对于输入值 5 和 8,跟踪表如下所示。
| 步骤 | 语句 | 备注 | a | b |
|---|---|---|---|---|
| 1 | a = int(input()) | 用户输入值 5 | 5 | ? |
| 2 | b = int(input()) | 用户输入值 8 | 5 | 8 |
| 3 | if a > 3: | 这将评估为 True | ||
| 4 | print("消息 #1") | 它显示:消息 #1 | ||
| 5 | print("结束!") | 它显示:结束! |
注意,尽管第二个布尔表达式(a > 4 and b <= 10)也可能评估为 True,但它从未被检查过。
ii)对于输入值 -13 和 0,跟踪表如下所示。
| 步骤 | 语句 | 备注 | a | b |
|---|---|---|---|---|
| 1 | a = int(input()) | 用户输入值 -13 | -13 | ? |
| 2 | b = int(input()) | 用户输入值 0 | -13 | 0 |
| 3 | if a > 3: | 这将评估为 False | ||
| 4 | elif a > 4 and b <= 10: | 这将评估为 False | ||
| 5 | elif a * 2 == -26: | 这将评估为 True | ||
| 6 | print("消息 #4") | 它显示:消息 #4 | ||
| 7 | print("消息 #5") | 它显示:消息 #5 | ||
| 8 | b += 1 | -13 | 1 | |
| 9 | print("结束!") | 它显示:结束! |
注意,在第 8 步之后,第四个布尔表达式(b == 1)也可能评估为 True,但它从未被检查过。
iii)对于输入值 1 和 -1,跟踪表如下所示。
| 步骤 | 语句 | 备注 | a | b |
|---|---|---|---|---|
| 1 | a = int(input()) | 用户输入值 1 | 1 | ? |
| 2 | b = int(input()) | 用户输入值 -1 | 1 | -1 |
| 3 | if a > 3: | 这将评估为 False | ||
| 4 | elif a > 4 and b <= 10: | 这将评估为 False | ||
| 5 | elif a * 2 == -26: | 这将评估为 False | ||
| 6 | elif b == 1: | 这将评估为 False | ||
| 7 | print("消息 #7") | 它显示:消息 #7 | ||
| 8 | print("消息 #8") | 它显示:消息 #8 | ||
| 9 | print("结束!") | 它显示:结束! |
练习 18.1-2 星期几
设计一个流程图并编写相应的 Python 程序,提示用户输入一个介于 1 和 5 之间的整数,然后显示相应的工作日(星期一、星期二、星期三、星期四或星期五)。如果输入的值无效,必须显示错误消息。
解答
流程图和解决此练习的相应 Python 程序如下所示。

file_18.1-2
day = int(input("输入一个介于 1 和 5 之间的整数:"))
if day == 1:
print("星期一")
elif day == 2:
print("星期二")
elif day == 3:
print("星期三")
elif day == 4:
print("星期四")
elif day == 5:
print("星期五")
else:
print("无效数字")
练习 18.1-3 计数数字
编写一个 Python 程序,提示用户输入一个介于 0 和 999 之间的整数,然后计算它的总位数。最后,显示一条消息“你输入了一个 N 位数”,其中 N 是总位数。假设用户输入了一个介于 0 和 999 之间的有效整数。
解答
你可能正在试图找出如何使用除法操作来解决这个练习。你可能正在考虑将用户提供的整数除以 10,并检查整数商是否为 0。如果是,这意味着用户提供的整数是一位数。然后,你可以除以 100 或 1000 来检查两位数和三位数。你的想法部分是正确的,你的想法在下面的代码片段中得到了体现。
if x // 10 == 0:
digits = 1
elif x // 100 == 0:
digits = 2
elif x // 1000 == 0:
digits = 3
如果用户提供的整数(变量 x)是一位数,第一个布尔表达式计算结果为 True,其余的布尔表达式永远不会被检查!如果用户提供的整数是两位数,第一个布尔表达式计算结果为 False,第二个计算结果为 True,最后一个永远不会被检查!最后,如果用户提供的整数是三位数,第一个和第二个布尔表达式都计算结果为 False,最后一个计算结果为 True!
这似乎很准确,不是吗?那么,问题出在哪里?
考虑如果练习的措辞是“编写一个 Python 程序,提示用户输入一个整数,当用户提供的整数由两位数字组成时显示一条消息”。很可能会按照以下步骤进行:
x = int(input("输入一个整数:"))
if x // 100 == 0:
print("输入了一个 2 位整数")
然而,这段代码是有缺陷的!虽然布尔表达式 x // 100 == 0 对于所有两位数或更多位的用户提供的整数都工作得很好,但不幸的是,它对于一位数(因为它对于它们不计算为 False)不起作用。因此,使用整数除法不是正确的方法。正确的解决方案比你想象的要简单得多!
你能想到的最小的两位整数是什么?是 10,对吧?你能想到的最大的是多少?是 99,对吧?所以,正确的解决方案如下。
x = int(input("输入一个整数:"))
if 10 <= x <= 99:
print("输入了一个 2 位整数")
根据所有这些,练习的完整解决方案如下!
file_18.1-3a
x = int(input("输入一个整数(0 - 999):"))
if 0 <= x <= 9:
digits = 1
elif 10 <= x <= 99:
digits = 2
else:
digits = 3
print("输入了一个{}位整数".format(digits), sep = "")
如果你想让你的程序更好,并在用户输入不在 0 到 999 之间的值时向用户显示错误消息,你可以这样做:
file_18.1-3b
x = int(input("输入一个整数(0 - 999):"))
if 0 <= x <= 9:
print("输入了一个 1 位整数")
elif 10 <= x <= 99:
print("输入了一个 2 位整数")
elif 100 <= x <= 999:
print("输入了一个 3 位整数")
else:
print("输入错误!")
18.2 复习问题:正确/错误
对以下每个陈述选择正确或错误。
1)多重选择决策结构用于扩展选择数量。
2)多重选择决策结构最多可以有三种选择。
3)在多重选择决策结构中,一旦布尔表达式评估为真,下一个布尔表达式也会被评估。
4)在多重选择决策结构中,位于 else 关键字下面的最后一个语句或语句块 N+1 总是会被执行。
5)在多重选择决策结构中,当至少一个之前的布尔表达式评估为真时,会执行最后一个语句或语句块 N+1(位于 else 关键字下面)。
6)在多重选择决策结构中,最后一个语句或语句块 N+1,以及扩展的 else 关键字,可以被省略。
7)在以下代码片段中,只有当变量 a 包含的值不是 1、2 或 3 时,才会执行语句 y += 1。
if a == 1:
x += 5
elif a == 2:
x -= 2
elif a == 3:
x -= 9
else:
x += 3
y += 1
8)在上一个练习的代码片段中,只有当变量 a 包含的值不是 1、2 或 3 时,才会执行语句 x += 3。
18.3 复习练习
Complete the following exercises.
1)创建一个跟踪表,以确定在下一个 Python 程序的四个不同执行中每一步的变量值。
The input values for the four executions are: (i) 5, (ii) 150, (iii) 250, and (iv) −1.
q = int(input())
if 0 < q <= 50:
b = 1
elif 50 < q <= 100:
b = 2
elif 100 < q <= 200:
b = 3
else:
b = 4
print(b)
2)创建一个跟踪表,以确定在下一个 Python 程序的三个不同执行中每一步的变量值。
The input values for the three executions are: (i) 5, (ii) 150, and (iii) −1.
amount = float(input())
discount = 0
if amount < 20:
discount = 0
elif 20 <= amount < 60:
discount = 5
elif 60 <= amount < 100:
discount = 10
elif amount >= 100:
discount = 15
payment = amount - amount * discount / 100
print(discount, ",", payment)
3)创建一个跟踪表,以确定在下一个 Python 程序的三个不同执行中每一步的变量值。然后,设计相应的流程图。
The input values for the three executions are: (i) 1, (ii) 3, and (iii) 250.
a = int(input())
x = 0
y = 0
if a == 1:
x = x + 5
y = y + 5
elif a == 2:
x = x - 2
y -= 1
elif a == 3:
x = x - 9
y = y + 3
else:
x = x + 3
y += 1
print(x, ",", y)
4)创建一个跟踪表,以确定在下一个 Python 程序的三个不同执行中每一步的变量值。然后,设计相应的流程图。
The input values for the three executions are: (i) 10, 2, 5; (ii) 5, 2, 3; and (iii) 4, 6, 2.
a = int(input())
x = int(input())
y = float(input())
if a == 10:
x = x % 2
y = y ** 2
elif a == 3:
x = x * 2
y -= 1
elif a == 5:
x = x + 4
y += 7
else:
x -= 3
y += 1
print(x, ",", y)
5)使用正确的缩进来编写以下 Python 程序。
a = float(input())
if a < 1:
y = 5 + a
print(y)
elif a < 5:
y = 23 / a
print(y)
elif a < 10:
y = 5 * a
print(y)
else:
print("Error!")
6)编写一个 Python 程序,提示用户输入两个整数,然后显示一条消息,指出两个数是否都是奇数或都是偶数;否则显示消息“没有特殊之处”。
7)两支足球队在欧洲冠军联赛中对战。编写一个 Python 程序,提示用户输入两队的名称以及每队得分,然后当两队得分相等时显示获胜队的名称或消息“平局!”假设用户输入了有效的值。
8)设计一个流程图并编写相应的 Python 程序,允许用户输入一个介于-9999 和 9999 之间的整数,然后计算其总位数。最后,显示消息“您输入了一个 N 位数”,其中 N 是总位数。假设用户输入了-9999 和 9999 之间的有效整数。
9)重写上一个练习的 Python 程序以验证数据输入。当用户输入无效值时,必须显示错误消息。
10)编写一个 Python 程序,显示以下菜单:
1)将美元转换为欧元 (EUR)
2)将美元转换为英镑 (GBP)
3)将美元转换为日元 (JPY)
4)将美元转换为加拿大元 (CAD)
然后提示用户输入一个选择(1、2、3 或 4)和美元金额,计算并显示所需的价值。假设用户输入了有效的值。已知
►$1 = 0.94 欧元 (EUR)
►$1 = 0.81 英镑 (£)
►$1 = ¥ 149.11 日元 (JPY)
►$1 = 1.36 加拿大元 (CAD)
11)编写一个 Python 程序,提示用户输入 1 到 12 月份的数字,然后显示相应的季节。假设用户输入了有效的值。已知
►冬季包括 12 月、1 月和 2 月
►春季包括 3 月、4 月和 5 月
►夏季包括 6 月、7 月和 8 月
►秋季包括 9 月、10 月和 11 月
12)重写上一个练习的 Python 程序以验证数据输入。当用户输入无效值时,必须显示错误消息。
13)编写一个 Python 程序,提示用户输入月份的名称,然后显示相应的数字(1 代表一月,2 代表二月,依此类推)。如果输入的值无效,必须显示错误消息。
14)美国最流行和常用的评分系统使用字母等级的离散评估。设计一个流程图并编写相应的 Python 程序,提示用户输入一个介于 A 和 F 之间的字母,然后根据以下表格显示相应的百分比。
| 等级 | 百分比 |
|---|---|
| A | 90 ‐ 100 |
| B | 80 ‐ 89 |
| C | 70 ‐ 79 |
| D | 60 ‐ 69 |
| E / F | 0 ‐ 59 |
假设用户输入了有效的值。
15)设计一个流程图并编写相应的 Python 程序,显示以下菜单:
1)将英里转换为码
2)将英里转换为英尺
3)将英里转换为英寸
然后提示用户输入选择(1、2 或 3)和距离(英里)。然后,它计算并显示所需值。假设用户输入了有效的距离值。然而,如果输入的选择无效,必须显示错误信息。已知
►1 英里 = 1760 码
►1 英里 = 5280 英尺
►1 英里 = 63360 英寸
16)罗马数字如下表所示。
| 数字 | 罗马数字 |
|---|---|
| 1 | I |
| 2 | II |
| 3 | III |
| 4 | IV |
| 5 | V |
| 6 | VI |
| 7 | VII |
| 8 | VIII |
| 9 | IX |
| 10 | X |
编写一个 Python 程序,提示用户输入一个介于 I 和 X 之间的罗马数字,然后显示相应的数字。然而,如果输入的选择无效,必须显示错误信息。
17)在线超市根据客户每月购买的酒瓶总数来奖励积分。积分的分配如下:
►如果客户购买 1 瓶酒,他们将获得 3 分。
►如果客户购买 2 瓶酒,他们将获得 10 分。
►如果客户购买 3 瓶酒,他们将获得 20 分。
►如果客户购买 4 瓶或更多的酒,他们将获得 45 分。
使用情况决策结构,编写一个 Python 程序,提示用户输入他们在一个月内购买的酒瓶总数,然后显示获得的积分数。假设用户输入了有效的值。
18)编写一个 Python 程序,提示用户输入他们的名字,然后随机显示“Hello NN!”、“Hi NN!”或“What's up NN!”,其中 NN 是用户的名字。要显示的消息必须随机选择。
19)编写一个 Python 程序,允许用户输入一个如“zero”、“one”或“two”之类的单词,然后将其转换为相应的数字,如 0、1 或 2。这必须对 0 到 9 的数字都这样做。当用户输入未知数字时,显示“I don't know this number!”。
20)蒲福风级(^([15]))是一种经验测量方法,它将风速与陆地或海洋上的观察条件联系起来。编写一个 Python 程序,提示用户输入蒲福风级,然后显示以下表格中的相应描述。然而,如果输入的数字无效,必须显示错误信息。
| 蒲福风级 | 描述 |
|---|---|
| 0 | 平静 |
| 1 | 微风 |
| 2 | 微风 |
| 3 | 微风 |
| 4 | 中等风 |
| 5 | 新风 |
| 6 | 强风 |
| 7 | 中等风暴 |
| 8 | 风暴 |
| 9 | 强风暴 |
| 10 | 风暴 |
| 11 | 狂风暴雨 |
| 12 | 飓风力量 |
21)编写一个 Python 程序,提示用户输入一个介于 0.0 和 9.9 之间的一位小数的数字,然后以英文文本的形式显示该数字。例如,如果用户输入 2.3,程序必须显示“Two point three”。假设用户输入了有效的值。
提示:避免逐个检查每个实数,因为这需要具有 100 个案例的多重选择决策结构!尝试找到更高效和巧妙的方法吧!
第十九章
嵌套决策控制结构
19.1 嵌套决策控制结构是什么?
嵌套决策控制结构是“嵌套”(包含)在另一个决策控制结构中的决策控制结构。这意味着一个决策控制结构可以嵌套(包含)另一个决策控制结构(然后成为“嵌套”决策控制结构)。反过来,那个嵌套的决策控制结构可以包含另一个决策结构,依此类推。
嵌套决策控制结构的示例如下。

这可以被重新排列为

并且 Python 代码如下所示。
if x < 30:
if x < 15: [更多…]
y = y + 2
else:
y -= 1
else:
y += 1
嵌套的深度没有实际限制。只要不违反语法规则,你可以嵌套任意数量的决策控制结构。然而,出于实际考虑,当你移动到三或四个嵌套级别时,整个结构会变得非常复杂且难以理解。
复杂的代码可能导致无效的结果!尽量保持代码尽可能简单,通过将大型嵌套决策控制结构分解成多个较小的结构,或者使用其他类型的决策控制结构。
显然,只要保持它们在语法和逻辑上的正确性,你可以在任何决策控制结构内部嵌套任何其他决策控制结构。在下一个示例中,一个多分支决策结构嵌套在一个双分支决策结构中。
file_19.1
x = int(input("输入一个选择:"))
if x < 1 or x > 4:
print("无效的选择")
else:
print("有效的选择")
if x == 1: [更多…]
print("第一个选项被选中")
elif x == 2:
print("第二个选项被选中")
elif x == 3:
print("第三个选项被选中")
elif x == 4:
print("第四个选项被选中")
注意,在多分支决策结构中缺少关键字 else。如果你希望包含它,考虑到只有 1、2、3 或 4 这几个选择被检查,你可以将 elif x == 4 替换为 else。
练习 19.1-1 跟踪表和嵌套决策控制结构
创建一个跟踪表,以确定在下一个 Python 程序的三个不同执行中每个步骤中变量的值。
三次执行的输入值分别为:(i) 13,(ii) 18,和 (iii) 30。
file_19.1-1
x = int(input())
y = 10
if x < 30:
if x < 15:
y = y + 2
else:
y -= 1
else:
y += 1
print(y)
解答
i)对于输入值 13,跟踪表如下所示。
| 步骤 | 语句 | 备注 | x | y |
|---|---|---|---|---|
| 1 | x = int(input()) | 用户输入值 13 | 13 | ? |
| 2 | y = 10 | 13 | 10 | |
| 3 | if x < 30: | 这评估为 True | ||
| 4 | if x < 15: | 这评估为 True | ||
| 5 | y = y + 2 | 13 | 12 | |
| 6 | print(y) | 显示:12 |
ii)对于输入值 18,跟踪表如下所示。
| 步骤 | 语句 | 备注 | x | y |
|---|---|---|---|---|
| 1 | x = int(input()) | 用户输入值 18 | 18 | ? |
| 2 | y = 10 | 18 | 10 | |
| 3 | if x < 30: | 这评估为 True | ||
| 4 | if x < 15: | 这评估为 False | ||
| 5 | y -= 1 | 18 | 9 | |
| 6 | print(y) | 它显示:9 |
iii)对于输入值 30,跟踪表如下所示。
| 步骤 | 语句 | 备注 | x | y |
|---|---|---|---|---|
| 1 | x = int(input()) | 用户输入值 30 | 30 | ? |
| 2 | y = 10 | 30 | 10 | |
| 3 | if x < 30: | 这评估为 False | ||
| 4 | y += 1 | 30 | 11 | |
| 5 | print(y) | 它显示:11 |
练习 19.1-2 正数、负数或零?
设计一个流程图并编写相应的 Python 程序,允许用户输入一个数字,然后根据用户提供的值是大于、小于还是等于零来显示消息“正数”、“负数”或“零”。
解答
流程图如下所示。

这个流程图可以用嵌套决策控制结构或多重选择决策结构来编写 Python 程序。让我们都试试!
第一种方法 – 使用嵌套决策控制结构
file_19.1-2a
a = float(input())
if a > 0:
print("Positive")
else:
if a < 0:
print("Negative")
else:
print("Zero")
第二种方法 – 使用多重选择决策结构
file_19.1-2b
a = float(input())
if a > 0:
print("Positive")
elif a < 0:
print("Negative")
else:
print("Zero")
19.2 复习问题:对/错
对以下每个陈述选择正确或错误。
1)决策控制结构的嵌套描述了一种情况,其中一个或多个决策控制结构的路径包围了其他决策控制结构。
2)嵌套级别可以深到程序员想要的程度。
3)当一个问题可以使用多重选择决策结构或嵌套决策控制结构来解决时,第二种选择更好,因为程序的可读性更高。
4)可以在简单选择决策结构内嵌套多重选择决策结构,但不能反过来。
19.3 复习练习
完成以下练习。
1)创建一个跟踪表以确定下一次 Python 程序在四次不同执行中每一步的变量值。
四次执行的输入值分别为:(i) 20, 1; (ii) 20, 3; (iii) 12, 8; 和 (iv) 50, 0。
x = int(input())
y = int(input())
if x < 30:
if y == 1:
x = x % 3
y = 5
elif y == 2:
x = x * 2
y = 2
elif y == 3:
x = x + 5
y += 3
else:
x -= 2
y += 1
else:
y += 1
print(x, ",", y)
2)创建一个跟踪表以确定下一次 Python 程序在四次不同执行中每一步的变量值。
四次执行的输入值分别为:(i) 60, 25; (ii) 50, 8; (iii) 20, 15; 和 (iv) 10, 30。
x = int(input())
y = int(input())
if (x + y) / 2 <= 20:
if y < 10:
x = x % 3
y += 2
elif y < 20:
x = x * 5
y += 2
else:
x = x - 2
y += 3
else:
if y < 15:
x = x % 4
y = 2
elif y < 23:
x = x % 2
y -= 2
else:
x = 2 * x + 5
y += 1
print(x, ",", y)
3)使用正确的缩进编写以下 Python 程序。
a = int(input())
if a > 1000:
print("Big Positive")
else:
if a > 0:
print("Positive")
else:
if a < -1000:
print("Big Negative")
else:
if a < 0:
print("Negative")
else:
print("Zero")
4)在希腊,您至少 16 岁时可以驾驶小型滑板车,而您至少 18 岁时可以驾驶汽车。编写一个 Python 程序,提示用户输入他们的年龄,然后根据用户的年龄显示以下消息之一:
►“您不能驾驶小型滑板车或汽车”,当用户年龄小于 16 岁时
►“您可以在 16 至 18 岁之间驾驶小型滑板车”,当用户年龄在 16 至 18 岁之间时
►“您可以驾驶汽车和一辆小型滑板车”,当用户年龄为 18 岁或以上时
当用户输入无效值时,必须显示错误消息。
5)hoverboard 工厂经理需要一个程序来计算工厂在一个月期间所获得的利润或亏损。以下是一些信息:
►制造每台 hoverboard 的成本为 150 美元。
►hoverboard 售价为每台 250 美元。
►该工厂每月为每位员工支付 1000 美元的保险费。
编写一个 Python 程序,提示用户输入销售的 hoverboard 数量和公司中的员工数量。根据公司的财务表现,程序必须然后显示以下消息之一:
►盈利
►亏损
►盈亏平衡
当用户输入销售的 hoverboard 数量为负数或员工数量非正数([1](footnotes.html#Endnote_16))时,必须显示错误消息。
6)编写一个 Python 程序,提示用户输入他们的名字。程序必须然后选择一个介于 1 到 24 之间的随机整数来代表一个小时,然后,它必须显示消息“现在是 HH:00”以及,根据该数字,显示“早上好 NN!”、“晚上好 NN!”、“下午好 NN!”或“晚上好 NN!”,其中 HH 是随机选择的数字,NN 是用户的名字。解决这个练习两次,一次使用嵌套决策结构,一次使用多选决策结构。
7)编写一个 Python 程序,提示用户输入三角形的三个边的长度,然后确定用户提供的数字是否可以是三角形三边的长度。如果长度无效,必须显示相应的消息;否则,程序必须进一步确定三角形是
a)等边
提示:在等边三角形中,所有边都相等。
b)直角(或直角三角形)
提示:使用勾股定理。
c)非特殊
提示:在任何三角形中,每边的长度都小于其他两边长度之和。
-
在自动取款机(ATM)中,有 10 美元、5 美元和 1 美元的纸币。编写一个 Python 程序来模拟这种 ATM 的工作方式。一开始,机器提示用户输入四位数密码,然后检查密码的有效性(假设“1234”是有效的密码)。如果用户提供的密码正确,程序必须提示用户输入他们想要取出的金额(一个整数值),最后显示 ATM 必须发放的最少纸币数量。例如,如果用户输入金额为 36 美元,程序必须显示“3 张 10 美元纸币,1 张 5 美元纸币和 1 张 1 美元纸币”。此外,如果用户输入错误的密码,机器将允许他们重试两次。如果用户三次都输入了错误的密码,必须显示消息“密码锁定”,并且程序必须结束。假设用户输入了有效的金额值。
-
编写一个 Python 程序,提示用户输入两个值,一个用于温度,另一个用于风速。如果温度高于 75 华氏度,则认为这一天很热,否则是冷天。如果风速高于每小时 12 英里,则认为这一天有风,否则是无风。程序必须根据用户提供的值显示一条单一的消息。例如,如果用户输入温度为 60 华氏度,风速为 10 英里每小时,程序必须显示“这一天很冷且无风”。假设用户输入了有效的值。
第二十章
关于带有决策控制结构的流程图的更多信息
20.1 简介
通过学习前面的章节,你已经熟悉了所有的决策控制结构。由于流程图是学习“算法思维”和帮助你更好地理解特定控制结构的理想方式,本章致力于教你如何将 Python 程序转换为流程图,或将流程图转换为 Python 程序。
20.2 将 Python 程序转换为流程图
要将 Python 程序转换为相应的流程图,你需要回忆所有的决策控制结构和它们对应的流程图片段。它们都总结在这里。
单一选择决策结构

双重选择决策结构

多重选择决策结构

练习 20.2-1 设计流程图
设计与以下 Python 程序相对应的流程图。
x = float(input())
z = x ** 3
w = (z - 4) * (x - 3) / 7 + 36
if z >= w and x < z:
y = 2 * x
if y > 0: #这是一个嵌套的单一选择决策结构
y += 1
else:
y = 4 * x
a += 1
print(y)
解决方案
在这个 Python 程序中,有一个单一选择决策结构嵌套在双重选择决策结构中。其相应的流程图如下。

流程图是表示算法的一种非常宽松的方法。因此,写 x³或甚至使用 Python 运算符(**)都是完全可以接受的。做你想做的任何事情;只要任何熟悉流程图的人都能清楚地理解你想表达的意思即可!
练习 20.2-2 设计流程图
设计与以下一般形式给出的代码片段相对应的流程图。
if Boolean_Expression_A:
语句块 A1
if Boolean_Expression_B:
语句块 B1
语句块 A2
else:
语句块 A3
if Boolean_Expression_C:
语句块 C1
else:
语句块 C2
解决方案
为了更好地观察,初始代码片段再次呈现,所有嵌套的决策控制结构都包含在矩形内。
if Boolean_Expression_A:
语句块 A1
if Boolean_Expression_B: [更多…]
语句块 B1
语句块 A2
else:
语句块 A3
if Boolean_Expression_C: [更多…]
语句块 C1
else:
语句块 C2
并且一般形式的流程图片段如下所示。

练习 20.2-3 设计流程图
设计与以下 Python 程序相对应的流程图。
a = float(input())
if a < 0:
y = a * 2
elif a < 10:
y = a / 2
elif a < 100:
y = a + 2
else:
b = float(input())
y = a * b
if y > 0: #这是一个嵌套的双重选择决策结构
y -= 1 #
else: #
y += 1 #
print(y)
解决方案
在这个 Python 程序中,一个双选决策结构嵌套在一个多选决策结构中。
流程图如下所示。

20.3 你可能会犯的一个错误!
在流程图中,新手程序员常见的错误是留下一些路径未连接,如下面的流程图所示。

请记住,每个路径都试图达到算法的末尾,因此你不能让任何一个路径未连接。
另一方面,尽量避免使用许多结束符号的流程图,如下所示,因为这些算法难以阅读和理解。

假设你正在设计一个流程图(见下方的流程图),并且你想要开始关闭其所有的决策控制结构。

只需记住,最后打开的决策控制结构必须是第一个关闭的!在这个例子中,最后一个决策控制结构是评估表达式 a < 30 的那个。这是你需要首先关闭的,如下所示。

接下来,你需要关闭第二个到最后一个决策控制结构,如下所示。

最后,你需要关闭第三个到最后一个决策控制结构,如下所示。

最后一个流程图可以被重新排列成如下所示的样式。

20.4 将流程图转换为 Python 程序
这种转换并不总是容易的。有些情况下,流程图设计者没有遵循特定的规则,因此初始流程图可能需要一些修改,才能将其转换为 Python 程序。以下是一个这样的例子。

如你所见,这个流程图片段中包含的决策控制结构与你已经学过的任何决策控制结构都不匹配。因此,你只有一个选择,那就是通过添加额外的语句或删除现有的语句来修改流程图,直到出现已知的决策控制结构。以下是一些需要修改初始流程图的练习。
练习 20.4-1 编写 Python 程序
编写与以下流程图相对应的 Python 程序。
.
解答
这相当简单。你必须克服的唯一障碍是真实和虚假路径并不完全在正确的位置。你需要使用真实路径,而不是虚假路径,来实际上包含单选决策结构中的语句。
有可能交换这两条路径,但你还需要否定相应的布尔表达式。以下两个流程图片段是等价的。

因此,流程图可以修改并看起来像这样。

相应的 Python 程序如下所示。
x = float(input())
y = 50
if x / 2 <= 10:
x = x / 3
y = x + 4
print(y)
练习 20.4-2 编写 Python 程序
编写与以下流程图相对应的 Python 程序。

解答
在这个练习中,有一个双分支决策结构嵌套在单分支决策结构中。你只需要否定布尔表达式 x == 100 并切换真/假路径。Python 程序如下所示。
x = float(input())
y = 1
if x != 100: # 这是一个单分支决策结构
y = float(input())
if x < y: # 这是一个嵌套的双分支决策结构
x = x - 3
y = x + 4
else:
x = x / 3 + 5
y = x + 20
print(x, y)
练习 20.4-3 编写 Python 程序
编写与以下流程图相对应的 Python 程序。

解答
在这个流程图中,决策控制结构不匹配你学过的任何决策控制结构。因此,你必须通过添加额外的语句或删除现有的语句来修改流程图,直到出现已知的决策控制结构!
在这个练习中你必须克服的障碍是评估 y MOD x ≠ 1 布尔表达式的决策控制结构。注意,当执行流程遵循假路径时,它会执行语句 a ← 20,然后在其到达算法末尾之前执行语句 Write a。因此,如果你在它的假路径中简单地添加一个新的语句 Write a,你可以保持执行流程的完整性。以下流程图与初始流程图等价。

现在,流程图包括已知的决策控制结构;也就是说,一个双分支决策结构嵌套在另一个双分支决策结构中。相应的 Python 程序如下。
x = int(input())
y = int(input())
if x % y != 1:
if y % x != 1:
print("无效")
else:
a = 20
print(a)
else:
a = 10
print(a)
然而,你可以做更好的事情!如果你否定所有的布尔表达式并切换它们的真/假路径,你可以得到一个多分支决策结构,这在 Python 中比嵌套决策控制结构更方便。修改后的流程图如下所示。

相应的 Python 程序如下。
x = int(input())
y = int(input())
if x % y == 1:
a = 10
print(a)
elif y % x == 1:
a = 20
print(a)
else:
print("无效")
20.5 复习练习
完成以下练习。
- 设计与以下 Python 程序相对应的流程图。
a = int(input())
if a % 10 == 0:
a += 1
print("消息 #1")
if a % 3 == 1:
a += 5
print("消息 #2")
if a % 3 == 2:
a += 10
print("消息 #3")
print(a)
- 设计与以下 Python 程序相对应的流程图。
a = int(input())
if a % 10 == 0:
a += 1
print("消息 #1")
if a % 3 == 1:
a += 5
print("消息 #2")
else:
a += 7
print(a)
- 设计与以下 Python 程序相对应的流程图。
a = float(input())
if a < 0:
y = a * 2
if y > 0:
y +=2
elif y == 0:
y *= 6
else:
y /= 7
elif a < 22:
y = a / 3
elif a < 32:
y = a - 7
else:
b = float(input())
y = a - b
print(y)
- 设计与以下通用形式代码片段相对应的流程图。
if Boolean_Expression_A:
if Boolean_Expression_B:
Block of statements B1
else:
Block of statements B2
Block of statements A1
else:
Block of statements A2
if Boolean_Expression_C:
Block of statements C1
elif Boolean_Expression_D:
Block of statements D1
else:
Block of statements E1
Block of statements A3
- 设计与以下 Python 程序相对应的流程图。
a = int(input())
y = 0
if a == 1:
y = a * 2
elif a == 2:
y = a - 3
elif a == 3:
y = a + 3
if y % 2 == 1:
y += 2
elif y == 0:
y *= 6
else:
y /= 7
elif a == 4:
b = float(input())
y = a + b + 2
print(y)
- 编写与以下流程图相对应的 Python 程序。

- 编写与以下流程图相对应的 Python 程序。

- 编写与以下流程图相对应的 Python 程序。

- 编写与以下流程图相对应的 Python 程序。

- 编写与以下流程图相对应的 Python 程序。

- 编写与以下流程图相对应的 Python 程序。

第二十一章
决策控制结构的技巧和窍门
21.1 简介
本章致力于教你一些有用的技巧和窍门,这些技巧和窍门可以帮助你编写“更好”的代码。在设计你自己的算法或甚至你自己的 Python 程序时,你应该始终牢记这些技巧。
这些技巧和窍门可以帮助你提高代码的可读性,并帮助使代码更短甚至更快。当然,没有单一完美的方法,因为有时使用特定的技巧或窍门可能会有帮助,但在另一种情况下,同样的技巧或窍门可能会有完全相反的结果。大多数时候,代码优化是编程经验的问题。
较小的算法并不总是解决特定问题的最佳方案。为了解决特定的问题,你可能编写一个非常短的算法,不幸的是,这个算法证明消耗了大量的 CPU 时间。另一方面,你可能用另一个算法解决相同的问题,尽管它看起来更长,但计算结果要快得多。
21.2 选择决策控制结构
下图可以帮助你根据检查的变量数量决定哪种决策控制结构更适合给定的问题。

此图推荐最佳选项,而非唯一选项。例如,当有一个变量有超过两个情况时,使用嵌套决策控制结构并非错误。然而,所提出的多重选择决策结构更为方便。
21.3 简化决策控制结构
仔细查看以下给出的通用形式的流程图片段。

如你所见,在双分支决策控制结构的两条路径的开始和结束处都存在两个相同的语句或语句块。这意味着,无论布尔表达式的结果如何,这些语句都会被执行。因此,你可以简单地将它们移到外面,以及(分别)在双分支决策控制结构之前和之后,如图所示的这个等效结构。

同样的技巧可以应用于任何决策控制结构,只要所有路径中存在相同的语句或语句块。
有些情况下,这个技巧不能应用。例如,如果你不能移动一个语句(或语句块)在决策控制结构之前,如果这个语句影响了结构的布尔表达式。
你还是感到困惑吗?接下来,你将找到一些练习,可以帮助你更好地理解。
练习 21.3-1 “缩小”算法
使用更少的语句重新设计以下流程图。

解决方案
正如你所见,语句 y ← a * 2 存在于双分支决策结构的两条路径中。这意味着,无论布尔表达式的结果如何,这个语句都会被执行。因此,你可以简单地将这个语句移动到双分支决策结构的外面,并在其前面,如下所示。

练习 21.3-2 “缩小” Python 程序
使用更少的语句重写以下 Python 程序。
a = int(input())
if a > 0:
y = a * 4
print(y)
else:
y = a * 3
print(y)
解答
正如你所见,语句 print(y) 存在于双分支决策结构的两条路径中。这意味着,无论布尔表达式的结果如何,这个语句都会被执行。因此,你可以简单地将这个语句移动到双分支决策结构的外面,并紧随其后,如下所示。
a = int(input())
if a > 0:
y = a * 4
else:
y = a * 3
print(y)
练习 21.3-3 “缩小” 算法
使用更少的语句重新设计以下流程图,然后编写相应的 Python 程序。

解答
如果你尝试将写入 y 语句移出多分支决策结构,得到的流程图肯定与初始的流程图不等价。

这是因为右侧的最后一条路径,在初始流程图中没有包含写入 y 语句。
检查两个流程图是否产生相同的结果。例如,假设用户输入了一个错误的数字。在两个流程图中,执行流程都会转到写入 "Wrong Number" 语句。之后,初始流程图不再执行其他语句,而第二个流程图则执行了一个额外的写入 y 语句。
你不能将一个语句或语句块移出决策控制结构之外,如果它在所有路径中都不存在。
你现在可能想知道是否还有其他方法可以将写入 y 语句移出多分支决策结构。答案是“是的”,但你需要稍微调整流程图。你需要完全移除右侧的最后一条路径,并在开始时使用一个新的决策控制结构来检查用户提供的数字是否错误。一个可能的解决方案如下。

Python 程序如下
a = float(input())
if a >= 30:
print("Wrong Number")
else:
if a < 10:
y = a * 2
elif a < 20:
y = a / 2
else:
y = a + 2
print(y)
21.4 逻辑运算符 – 使用与否:这是一个问题!
在某些情况下,你可以使用逻辑运算符而不是嵌套的决策控制结构,这可以提高可读性。看看以下给出的通用形式的流程图片段。

如你所见,语句或语句块 1 仅在两个布尔表达式都评估为真时执行。语句或语句块 2 在所有其他情况下执行。因此,可以使用 AND 逻辑运算符重新设计此流程图片段。

现在,让我们看看另一个以一般形式给出的流程图片段。

在此流程图片段中,当布尔表达式 _1 或布尔表达式 _2 评估为真时,执行语句或语句块 2。因此,可以使用 OR 逻辑运算符重新设计此流程图片段,如下所示。

显然,这些方法也可以适应用于嵌套决策控制结构。
练习 21.4-1 代码重写
使用逻辑运算符重写以下 Python 程序。
today = input()
name = input()
如果今天是"February 16":
如果 name == "Loukia":
打印("Happy Birthday!!!")
否则:
打印("No match!")
否则:
打印("No match!")
解答
当两个布尔表达式都评估为真时,执行打印("Happy Birthday!!!")语句。当所有其他情况下,执行打印("No match!")语句。因此,可以使用 and 逻辑运算符重写 Python 程序。
today = input()
name = input()
如果今天是"February 16"并且名字是"Loukia":
打印("Happy Birthday!!!")
否则:
打印("No match!")
练习 21.4-2 代码重写
使用逻辑运算符重写以下 Python 程序。
a = int(input())
b = int(input())
y = 0
如果 a > 10:
y += 1
elif b > 20:
y += 1
否则:
y -= 1
打印(y)
解答
当变量 a 大于 10 或变量 b 大于 20 时,执行 y += 1 语句。因此,你可以使用 or 逻辑运算符重写 Python 程序。
a = int(input())
b = int(input())
y = 0
如果 a > 10 或 b > 20:
y += 1
否则:
y -= 1
打印(y)
21.5 合并两个或更多单分支决策结构
有时,你可能设计一个包含两个或更多连续单分支决策结构的算法,每个都评估相同的布尔表达式。以下是一个示例。

当出现这种情况时,你可以将所有单分支决策结构合并为一个,如下所示。

单分支决策结构需要彼此相邻。如果它们之间有任何语句,除非你能将这个语句移动到代码的另一个地方,否则你不能合并它们。
练习 21.5-1 合并决策控制结构
在以下 Python 程序中,合并单分支决策结构。
a = int(input())
如果 a > 0:
打印("Hello")
如果 a > 0:
打印("Hermes")
解答
第一个和第二个决策控制结构正在评估完全相同的布尔表达式,因此它们可以简单地合并成一个。
Python 程序变为
a = int(input())
if a > 0:
print("Hello")
print("Hermes")
练习 21.5-2 合并决策控制结构
在下面的 Python 程序中,尽可能多地合并单分支决策结构。
a = int(input())
y = 0
if a > 0:
y += a + 1
b = int(input()) [更多…]
如果不是(a <= 0):
print("Hello Hera")
a += 1 [更多…]
if a > 0:
print("Hallo Welt")
print(y)
解决方案
仔细观察后,很明显第一个和第二个决策控制结构正在评估完全相同的布尔表达式。具体来说,否定 a > 0 得到 a <= 0,a <= 0 的第二次否定(这次使用 not 操作符)得到 not(a <= 0)。因此,a > 0 实际上等同于 not(a <= 0)。
两次否定产生肯定。
然而,在第一个和第二个决策控制结构之间有语句 b = int(input()),这阻止了你将它们合并成一个。幸运的是,这个语句可以被移动到程序的开头,因为它实际上不影响执行流程的其余部分。
另一方面,在第二个和第三个决策控制结构之间有语句 a += 1,这也阻止了你合并;不幸的是,这个语句不能移动到其他地方,因为它确实影响了执行流程的其余部分(第二个和第三个决策控制结构依赖于这个语句)。因此,第三个决策控制结构不能与第一个和第二个合并!
最终的 Python 程序看起来是这样的。
a = int(input())
b = int(input())
y = 0
if a > 0:
y += a + 1
print("Hello Hera")
a += 1
if a > 0:
print("Hallo Welt")
print(y)
21.6 将两个单分支决策结构替换为双分支结构
看看下一个例子。
if x > 40:
做某事
if x <= 40:
做其他事情
第一个决策控制结构评估变量 x 以测试它是否大于 40,紧接着,第二个决策控制结构再次评估同一个变量以测试它是否小于或等于 40!
这是一种新手程序员非常常见的“错误”。他们使用两个单分支决策结构,尽管一个双分支决策结构就能完成相同的事情。
之前的例子可以用一个双分支决策结构重写,如下所示。
if x > 40:
做某事
else:
做其他事情
尽管这两个例子都是绝对正确的并且工作得很好,但第二个方案更好。CPU 只需要评估一个布尔表达式,这会导致更快的执行时间。
两个单分支决策结构必须相邻。如果它们之间有任何语句存在,你不能“合并”它们(即用双分支决策结构替换它们),除非你可以将这个语句移动到代码的其他地方。
练习 21.6-1 “合并”决策控制结构
在下面的 Python 程序中,尽可能多地“合并”单分支决策结构。
a = int(input())
y = 0
if a > 0:
y += a
b = int(input())
if not(a > 0):
print("Hello Zeus")
if y > 0:
print(y + 5)
y += a
if y <= 0:
print(y + 12)
解决方案
第一个决策控制结构评估变量 a 以测试它是否大于零,紧接着第二个决策控制结构再次评估变量 a 以测试它是否不大于零。尽管它们之间有 b = int(input())这个语句,但这个语句可以被移动到其他地方,因为它实际上不影响执行流程的其余部分。因此,第一个和第二个决策控制结构可以被合并!
另一方面,在第三个和第四个决策控制结构之间有 y += a 这个语句,这阻止了你合并。这个语句不能移动到其他地方,因为它确实影响了执行流程的其余部分(第三个和第四个决策控制结构依赖于这个语句)。因此,第三个和第四个决策控制结构不能合并!
最终的 Python 程序变为
a = int(input())
b = int(input())
y = 0
if a > 0:
y += a
else:
print("Hello Zeus")
if y > 0:
print(y + 5)
y += a
if y <= 0:
print(y + 12)
21.7 将最可能为真的布尔表达式放在最前面
多分支决策结构通常需要在决定执行哪个语句或语句块之前检查几个布尔表达式。在下一个决策控制结构中,
if Boolean_Expression_1:
第 1 个语句或语句块
elif Boolean_Expression_2:
第 2 个语句或语句块
elif Boolean_Expression_3:
第 3 个语句或语句块
程序首先测试布尔表达式 Boolean_Expression_1 是否为真。如果不是,它测试布尔表达式 Boolean_Expression_2 是否为真,如果不是,它测试布尔表达式 Boolean_Expression_3。然而,如果布尔表达式 Boolean_Expression_1 大部分时间都是假的,而布尔表达式 Boolean_Expression_3 大部分时间都是真的,这意味着在测试通常为真的布尔表达式 Boolean_Expression_3 之前,浪费了测试通常为假的布尔表达式 Boolean_Expression_1 的时间。
为了使你的程序更高效,你可以将最可能为真的布尔表达式放在前面,最可能为假的布尔表达式放在后面,如下所示。
if Boolean_Expression_3:
第 3 个语句或语句块
elif Boolean_Expression_2:
第 2 个语句或语句块
elif Boolean_Expression_1:
第 1 个语句或语句块
尽管这种改变可能看起来并不重要,但节省的每一丁点时间都可以让你的程序运行得更快、更高效。
练习 21.7-1 重新排列布尔表达式
根据研究,美国最受欢迎的宠物是狗,其次是猫,接下来是豚鼠,鹦鹉排在最后。在下面的 Python 程序中,重新排列布尔表达式,使程序在大多数情况下运行更快、更高效。
kind = input("你最喜欢的宠物是什么? ")
if kind == "Parrots":
print("它尖叫!")
elif kind == "Guinea pig":
print("它吱吱叫")
elif kind == "Dog":
print("它汪汪叫")
elif kind == "Cat":
print("它喵喵叫")
解答
对于这项研究,你可以重新排列 Python 程序,使其在大多数情况下运行得更快。
kind = input("你最喜欢的宠物是什么? ")
if kind == "Dog":
print("它汪汪叫")
elif kind == "Cat":
print("它喵喵叫")
elif kind == "Guinea pig":
print("它吱吱叫")
elif kind == "Parrots":
print("它尖叫!")
21.8 复习问题:判断题
选择以下每个陈述的真假。
1)较小的算法总是给定问题的最佳解决方案。
2)只要它在决策结构的两条路径的开始处存在,你就可以始终将一个语句移到双选择决策结构之前。
3)你可以始终使用逻辑运算符而不是嵌套决策控制结构来提高可读性。
4)只有当两个单选择决策结构连续排列并且评估等价的布尔表达式时,它们才能合并为一个单选择决策结构。
5)将双选择决策结构转换为两个单选择决策结构总是可能的。
6)只有当两个单选择决策结构连续排列并且评估相同的布尔表达式时,它们才能被一个双选择决策结构所替代。
21.9 复习问题:多项选择题
选择以下每个陈述的正确答案。
1)以下两个程序
a = int(input())
if a > 40:
print(a * 2)
a += 1
else:
print(a * 2)
a += 5
a = int(input())
print(a * 2)
if a > 40:
a += 1
else:
a += 5
a)产生相同的结果。
b)不会产生相同的结果。
c)以上都不是
2)以下两个程序
a = int(input())
if a > 40:
print(a * 2)
if a > 40:
print(a * 3)
a = int(input())
if a > 40:
print(a * 2)
print(a * 3)
a)产生相同的结果,但左边的程序更快。
b)产生相同的结果,但正确的程序更快。
c)不会产生相同的结果。
d)以上都不是
3)以下两个程序
a = int(input())
if a > 40:
print(a * 2)
else:
print(a * 3)
a = int(input())
if a > 40:
print(a * 2)
if a <= 40:
print(a * 3)
a)产生相同的结果,但左边的程序更快。
b)产生相同的结果,但正确的程序更快。
c)不会产生相同的结果。
d)以上都不是
21.10 复习练习
完成以下练习。
1)使用更少的语句重写以下 Python 程序。
y = int(input())
if y > 0:
x = int(input())
a = x * 4 * y
print(y)
a += 1
else:
x = int(input())
a = x * 2 * y + 7
print(y)
a -= 1
print(a)
2)使用更少的语句重新设计以下流程图。

3)使用更少的语句重写以下 Python 程序。
a = float(input())
if a < 1:
y = 5 + a
print(y)
elif a < 5:
y = 23 / a
print(y)
elif a < 10:
y = 5 * a
print(y)
else:
print("错误!")
4)使用逻辑运算符重写以下 Python 程序。
day = int(input())
month = int(input())
name = input()
if day == 16:
if month == 2:
if name == "Loukia":
print("生日快乐!!!")
else:
print("没有匹配!")
else:
print("没有匹配!")
else:
print("没有匹配!")
5)一位老师要求她的学生重写以下 Python 程序,不使用逻辑运算符。
a = float(input())
b = float(input())
c = float(input())
if a > 10 and c < 2000:
d = (a + b + c) / 12
print("结果是:", d)
else:
print("错误!")
一名学生编写了以下 Python 程序:
a = float(input())
b = float(input())
c = float(input())
if a > 10:
if c < 2000:
d = (a + b + c) / 12
print("结果是:", d)
else:
print("错误!")
确定程序对于所有可能的路径是否与老师提供的程序操作方式相同。如果不相同,尝试修改它并使其以相同的方式工作。
6)使用单分支决策结构重写以下 Python 程序。
a = float(input())
b = float(input())
c = float(input())
if a > 10:
if b < 2000:
if c != 10:
d = (a + b + c) / 12
print("结果是:", d)
else:
print("错误!")
7)在以下 Python 程序中,将两个单分支决策结构替换为一个双分支决策结构。
a = int(input())
y = 3
if a > 0:
y = y * a
b = int(input())
if not(a <= 0):
print("你好,宙斯")
print(y, b)
8)使用一个双分支决策结构重写以下 Python 程序。
a = float(input())
y = 0
if a > 0:
y = y + 7
b = float(input())
if not(a > 0):
print("你好,宙斯")
if a <= 0:
print(abs(a))
print(y)
9)根据 2013 年的研究,平板电脑上最受欢迎的操作系统是 iOS,其次是 Android,最后是微软 Windows。在以下 Python 程序中,重新排列布尔表达式,使程序在大多数情况下运行更高效。
os = input("你的平板电脑的操作系统是什么?")
if os == "Windows":
print("微软")
elif os == "iOS":
print("苹果")
elif os == "Android":
print("谷歌")
第二十二章
更多关于决策控制结构的内容
22.1 使用决策控制结构的简单练习
练习 22.1-1 它是一个整数吗?
编写一个 Python 程序,提示用户输入一个数字,然后显示一条消息,指出这个数字的数据类型是整数还是实数。
解答
人们都知道,一个数如果没有小数部分,那么它就是一个整数。在 Python 中,你可以使用 int()函数来获取任何实数的整数部分。如果用户提供的数字等于它的整数部分,那么这个数就被认为是整数。
例如,如果用户输入数字 7,这个数字和它的整数部分 int(7)是相等的。
另一方面,如果用户输入数字 7.3,这个数字和它的整数部分 int(7.3)是不相等的。
Python 程序如下所示。
file_22.1-1
x = float(input("请输入一个数字:"))
if x == int(x):
print(x, "是整数")
else:
print(x, "是实数")
请注意,在数据输入阶段使用了 float()函数。这是必要的,以便允许用户输入整数或浮点数。
练习 22.1-2 验证数据输入并找出奇数和偶数
设计一个流程图并编写相应的 Python 程序,提示用户输入一个非负整数,然后显示一条消息,指出这个数是偶数;如果不是,则显示“Odd”。此外,如果用户输入负值或浮点数,必须显示错误信息。
(这个练习让你在数据验证方面有所练习)。
解答
数据验证是限制数据输入的过程,迫使用户只能输入有效的值。
在这个练习中,你需要提示用户输入一个非负整数,如果他们输入负值或浮点数,则显示错误信息。解决这个练习的一般形式的流程图如下。

以下决策控制结构来自练习 17.1-4。它测试变量 x 是偶数还是奇数。

将两个流程图合并后,最终的流程图如下所示。

Python 程序如下所示。
file_22.1-2a
x = float(input("请输入一个整数:"))
if x < 0 or x != int(x):
print("Invalid Number")
else:
if x % 2 == 0:
print("Even")
else:
print("Odd")
除了使用嵌套决策结构外,你还可以使用多选决策结构,如下所示。
file_22.1-2b
x = float(input("请输入一个整数:"))
if x < 0 or x != int(x):
print("Invalid Number")
elif x % 2 == 0:
print("Even")
else:
print("Odd")
练习 22.1-3 收费员在哪里?
在收费站,有一个自动系统可以识别通过的车辆是摩托车、汽车还是卡车。编写一个 Python 程序,让用户输入车辆的类型(M 表示摩托车,C 表示汽车,T 表示卡车),然后根据以下表格显示司机必须支付的费用。
| Vehicle Type | Amount to Pay |
|---|---|
| Motorcycle | $1 |
| Car | $2 |
| Track | $4 |
程序必须能够在输入小写字母时正常工作。例如,程序必须对“M”或“m”都正常工作。然而,如果用户输入的字符不是 M、C 或 T(无论是大写还是小写),必须显示错误信息。
(Some more practice with data validation!)
Solution
这个问题的解决方案相当简单。唯一需要注意的事情是用户可能会输入大写字母 M、C 或 T,或者小写字母 m、c 或 t。程序需要接受这两种情况。为了处理这种情况,你可以使用 upper()方法将用户的输入转换为大写。然后你需要检查大写的 M、C 或 T 字符。
这里展示了 Python 程序。
file_22.1-3a
v = input().upper()
if v not in ["M", "C", "T"]: #只需检查大写 M、C 和 T
print("Invalid vehicle")
elif v == "M":
print("You need to pay $1")
elif v == "C":
print("You need to pay $2")
elif v == "T":
print("You need to pay $4")
注意 Python 如何将用户的输入转换为大写。
然而,如果你将多分支决策结构中的第一个情况移动到最后,就像这里所示,这个练习可以稍微高效地解决。
file_22.1-3b
v = input().upper()
if v == "M":
print("You need to pay $1")
elif v == "C":
print("You need to pay $2")
elif v == "T":
print("You need to pay $4")
else:
print("Invalid vehicle")
Exercise 22.1-4 The Most Scientific Calculator Ever!
编写一个 Python 程序来模拟电子计算器的功能。程序必须首先提示用户输入一个数字,然后输入操作类型(+、−、、/),最后输入第二个数字。随后,程序必须执行所选操作并显示结果。然而,如果用户输入的操作数不是+、−、或/,必须显示错误信息。
Solution
在这个练习中,你需要注意的唯一事情是用户可能会输入除数(第二个数字)为零的可能性。正如你所知,从数学的角度来看,除以零是不可能的。
以下 Python 程序使用多分支决策结构来检查操作类型。
file_22.1-4
a = float(input("Enter 1st number: "))
op = input("Enter type of operation: ") #变量 op 是字符串类型
b = float(input("Enter 2nd number: "))
if op == "+":
print(a + b)
elif op == "-":
print(a - b)
elif op == "*":
print(a * b)
elif op == "/":
if b == 0:
print("Error: Division by zero")
else:
print(a / b)
else:
print("Error: Invalid operand")
练习 22.1-5 将加仑转换为升,反之亦然
编写一个 Python 程序,显示以下菜单:
1)将加仑转换为升
2)将升转换为加仑
程序必须提示用户输入一个选择(1 或 2)和数量,然后计算并显示所需值。已知
1 加仑 = 3.785 升
解答
Python 程序如下所示。
file_22.1-5
COEFFICIENT = 3.785
print("1: Gallons to liters")
print("2: Liters to gallons")
choice = int(input("Enter choice: "))
quantity = float(input("Enter quantity: "))
if choice == 1:
result = quantity * COEFFICIENT
print(quantity, "gallons =", result, "liters")
else:
result = quantity / COEFFICIENT
print(quantity, "liters =", result, "gallons")
练习 22.1-6 将加仑转换为升,反之亦然(带有数据验证)
将上一个练习的 Python 程序重写为验证数据输入。当用户输入的选择不是 1 或 2,或输入的汽油数量为负数时,必须显示不同类型的输入错误消息。
解答
以下 Python 程序以通用形式给出,用于解决此练习。它提示用户输入一个选择。如果选择无效,则显示错误消息;否则,它提示用户输入数量。然而,如果输入的数量也无效,则显示另一个错误消息;否则,它根据用户的选择进行数据转换。
主代码
COEFFICIENT = 3.785
print("1: Gallons to liters")
print("2: Liters to gallons")
choice = int(input("Enter choice: "))
if choice not in [1, 2]:
print("Wrong choice!")
else:
quantity = float(input("Enter quantity: "))
if quantity < 0:
print("Invalid quantity!")
else:
代码片段 1:根据用户选择将加仑转换为升或升转换为加仑。
以下代码片段 1 是从上一个练习(练习 22.1-5)中提取的。它根据用户的选择将加仑转换为升,或将升转换为加仑。
代码片段 1
if choice == 1:
result = quantity * COEFFICIENT
print(quantity, "gallons =", result, "liters")
else:
result = quantity / COEFFICIENT
print(quantity, "liters =", result, "gallons")
在将代码片段 1 嵌入主代码后,最终的 Python 程序变为
file_22.1-6
COEFFICIENT = 3.785
print("1: Gallons to liters")
print("2: Liters to gallons")
choice = int(input("Enter choice: "))
if choice not in [1, 2]:
print("Wrong choice!")
else:
quantity = float(input("Enter quantity: "))
if quantity < 0:
print("Invalid quantity!")
else:
if choice == 1: [更多…]
result = quantity * COEFFICIENT
print(quantity, "gallons =", result, "liters")
else:
result = quantity / COEFFICIENT
print(quantity, "升 =", result, "加仑")
22.2 使用决策控制结构查找最小值和最大值
假设有一些人,你想找到最轻的那个人。假设他们一个接一个地来告诉你他们的体重。你必须做的是,记住第一个来的人的体重,对于每个新来的人,你必须将他/她的体重与你记住的体重进行比较。如果他/她更重,你忽略他的体重。然而,如果他/她更轻,你需要忘记之前的体重并记住新的体重。同样的程序会继续进行,直到所有的人都来过。
让我们随机让四个人来。假设他们的体重,按出现的顺序,是 165 磅、170 磅、160 磅和 180 磅。
| 程序 | 你心中变量 minimum 的值! |
|---|---|
| 第一个人来了。他重 165 磅。记住他的体重(想象一个名为 minimum 的变量)。 | minimum = 165 |
| 第二个人来了。他重 170 磅。他的体重不比你保存在变量 minimum 中的重量轻,所以你必须忽略他的体重。你心中的变量 minimum 仍然包含值 165。 | minimum = 165 |
| 第三个人来了。他重 160 磅,这个重量小于你保存在变量 minimum 中的重量,所以你必须忘记之前的值,并将变量 minimum 中的值保持为 160。 | minimum = 160 |
| 第四个人来了。他重 180 磅。他的体重不比你保存在变量 minimum 中的重量轻,所以你必须忽略他的体重。你心中的变量 minimum 仍然包含值 160。 | minimum = 160 |
当程序完成时,你心中的变量 minimum 包含了最轻人的体重!
以下是流程图和相应的 Python 程序,该程序提示用户输入四个人的体重,然后找到并显示最轻的体重。
.
file_22.2
print("请输入四个人的体重:")
w1 = int(input())
w2 = int(input())
w3 = int(input())
w4 = int(input())
记住第一个人的体重
minimum = w1
如果第二个人更轻,忘记之前的值
记住第一个人的体重
if w2 < minimum:
minimum = w2
如果第三个人更轻,忘记之前的值
记住第一个人的体重
if w3 < minimum:
minimum = w3
如果第四个人更轻,忘记之前的值
记住第一个人的体重
if w4 < minimum:
minimum = w4
print(minimum)
请注意,这个程序试图找出最低值,而不是这个值实际分配给了哪个变量。
你可以通过将所有布尔表达式中的“小于”运算符替换为“大于”运算符来找到最大值而不是最小值。
练习 22.2-1 找到最重的人的名字
编写一个 Python 程序,提示用户输入三个人的体重和姓名,然后显示最重的人的姓名和体重。
解决方案
在这个练习中,除了最大体重外,还需要将实际拥有该体重的人的名字存储在另一个变量中。Python 程序如下所示。
file_22.2-1
w1 = int(input("输入第一个人的体重:"))
n1 = input("输入第一个人的名字:")
w2 = int(input("输入第二个人的体重:"))
n2 = input("输入第二个人的名字:")
w3 = int(input("输入第三个人的体重:"))
n3 = input("输入第三个人的名字:")
maximum = w1 #记住体重
mName = n1 #以及第一个人的名字。
if w2 > maximum: #如果第二个人更重,则忽略之前的值,并
maximum = w2 #记住体重
mName = n2 #以及第二个人的名字。
if w3 > maximum: #如果第三个人更重,则忽略之前的值,并
maximum = w3 #记住体重
mName = n3 #以及第三个人的名字。
print("体重最重的人是", mName)
print("他们的体重是", maximum)
如果两个体重最重的人恰好体重相同,则找到并显示第一个人的名字。
22.3 决策控制结构在解决数学问题中的应用
练习 22.3-1 求 y 的值
设计一个流程图并编写相应的 Python 程序,以找到并显示以下公式的 y 值(如果可能)。
.
解答
在这个练习中,防止用户输入 0 或 4 的值至关重要,因为这些值会导致分母为零。因此,程序需要考虑这些限制。流程图如下所示。

以及 Python 程序如下所示。
file_22.3-1
x = float(input())
if x == 0 or x == 4:
print("错误:除以零!")
else:
y = (5 + x) / x + (x + 9) / (x - 4)
print(y)
练习 22.3-2 求 y 的值
设计一个流程图并编写相应的 Python 程序,以找到并显示以下公式的 y 值(如果可能)。
.
解答
该公式有两个不同的结果。
►当 x 大于或等于零时,
中 y 的值可以按照前一个练习中所示的方法找到。
►然而,对于小于零的 x,一个小的细节可以节省你一些代码行。仔细观察后,很明显,分数
没有限制,因为 x 永远不会是+5;因此,分母永远不会为零。这是因为给定的公式中 x 小于零!
流程图如下所示。

该 Python 程序如下所示。
file_22.3-2
x = float(input())
if x >= 0:
if x == 0 or x == 3:
print("错误:除以零!")
else:
y = (7 + x) / (x - 3) + (3 - x) / x
print(y)
else:
y = 40 * x / (x - 5) + 3
print(y)
练习 22.3-3 解线性方程 ax + b = 0
设计一个流程图并编写相应的 Python 程序,以找到并显示线性方程的根
ax + b = 0
解答
在方程 ax + b = 0 中,系数 a 和 b 是已知的实数,x 代表要找到的未知量。因为 x 是一次幂,所以这个方程被归类为一次方程,也称为线性方程。
方程的根是满足这个方程的 x 的值;也就是说,等式 ax + b 的左边等于零。
在这个练习中,用户必须输入系数 a 和 b 的值,程序必须找到使得 ax + b 等于零的 x 的值。
当解方程 ax + b = 0 时,对于 x 的解变为 x = −b / a。根据用户输入的数据,可能出现三种可能的情况:
i)用户可能输入系数 a 的值为 0,而系数 b 的值为非零值。在这种情况下,x = −b / a 的结果是未定义的。正如您从数学中已经知道的,除以零是无法进行的。
ii)用户可能输入系数 a 和 b 的值都为 0。在这种情况下,x = −b / a 的结果没有定义的值,这被称为不定形式。
iii)用户可能输入任何其他一对值。
下面使用多重选择决策结构表示这三种情况和相应的路径。


这里展示了 Python 程序。
file_22.3-3
print("输入系数 a 和 b 的值")
a, b = float(input()), float(input())
if a != 0:
x = -b / a
print(x)
elif b != 0:
print("未定义")
else:
print("不定形式")
练习 22.3-4 解二次方程 ax² + bx + c = 0
设计一个流程图并编写相应的 Python 程序,以找到并显示二次方程的根
ax² + bx + c = 0
解答
在方程 ax² + bx + c = 0 中,系数 a、b 和 c 是已知的实数,x 代表要找到的未知量。因为 x 是平方的,所以这个方程被归类为二次方程,也称为二次方程。
方程的根是满足这个方程的 x 的值;也就是说,等式 ax² + bx + c 的左边等于零。
在这个练习中,用户必须输入系数 a、b 和 c 的值,程序必须找到使得 ax² + bx + c 等于零的 x 的值。
根据系数 a 的值,这个问题可以分为两个独立的子问题。
i)如果系数 a 不等于零,可以使用判别式 D 找到方程的根。请注意,下面这个练习的解决方案在 D < 0 时找不到复数根;这超出了本书的范围。
ii)如果系数 a 等于零,方程变为线性方程 bx + c = 0,其解已在之前的练习中提供(练习 22.3-3)。
所有必要的路径都显示在此处。

右侧路径(a = 0)是线性方程 bx + c = 0 的解。
使用此图可以设计以下流程图。

The Python program is shown here.
file_22.3-4
from math import sqrt
print("输入系数 a,b 和 c 的值")
a, b, c = float(input()), float(input()), float(input())
if a != 0:
D = b ** 2 - 4 * a * c
if D >= 0:
if D > 0:
x1 = (-b + sqrt(D)) / (2 * a)
x2 = (-b - sqrt(D)) / (2 * a)
print("Roots:", x1, ",", x2)
else:
x = -b / (2 * a)
print("One double root:", x)
else:
print("Complex Roots")
else:
if b != 0:
x = -c / b
print("Root:", x)
elif c != 0:
print("Undefined")
else:
print("Indeterminate form")
22.4 使用连续值范围的练习
正如您已经看到的,在许多问题中,变量的值或表达式的结果可以定义必须执行哪个语句或语句块。在接下来的练习中,您将学习如何测试一个值或表达式的结果是否属于特定的值范围(一系列连续的值范围)。
假设您想显示一条消息,指示女性在不同温度下可能穿的衣服类型。
| 室外温度(华氏度) | 女性可能穿的衣服类型 |
|---|---|
| 温度 < 45 | 毛衣,大衣,牛仔裤,衬衫,鞋子 |
| 45 ≤ 温度 < 65 | 毛衣,牛仔裤,夹克,鞋子 |
| 65 ≤ 温度 < 75 | 七分裤,短裤,T 恤,无袖衫,人字拖,运动鞋 |
| 75 ≤ 温度 | 短裤,T 恤,无袖衫,半身裙,裙子,人字拖 |
初看起来,单分支决策结构可能似乎是逻辑选择。虽然并不错误,但更深入的分析揭示,每个条件都是相互依赖的,这意味着当其中一个评估为 True 时,其他都不应该被评估。您需要从一组可能性中选择一个替代方案。
解决此类练习,您可以使用多分支决策结构或嵌套决策控制结构。然而,前者是最佳选择,因为它更方便,并且可以提高可读性,正如您在下面的代码片段中可以看到。
if 温度 < 45:
print("毛衣,大衣,牛仔裤,衬衫,鞋子")
elif 温度 >= 45 且 温度 < 65:
print("毛衣,牛仔裤,夹克,鞋子")
elif 温度 >= 65 且 温度 < 75:
print("七分裤,短裤,T 恤,无袖衫,人字拖,运动鞋")
elif 温度 >= 75:
print("短裤,T 恤,无袖衫,半身裙,裙子,人字拖")
然而,仔细检查后,很明显所有下划线的布尔表达式实际上并不需要。例如,如果第一个布尔表达式(temperature < 45)评估为 False,则执行流程将继续评估第二个布尔表达式。然而,由于第一个布尔表达式已经评估为 False,变量 temperature 肯定大于或等于 45。因此,当评估布尔表达式 temperature >= 45 时,肯定为 True,因此可以省略。相同的逻辑适用于所有情况;可以省略所有下划线的布尔表达式。最终的代码片段如下,已移除所有不必要的评估。
if temperature < 45:
print("毛衣,外套,牛仔裤,衬衫,鞋子")
elif temperature < 65:
print("毛衣,牛仔裤,夹克,鞋子")
elif temperature < 75:
print("七分裤,短裤,T 恤,无袖衫,人字拖,运动鞋")
else:
print("短裤,T 恤,无袖衫,半身裙,裙子,人字拖")
练习 22.4-1 计算折扣
根据客户订单的总金额,客户将获得相应的折扣。如果订单总金额少于 30 美元,则不提供折扣。如果订单总金额等于或大于 30 美元且小于 70 美元,则应用 5%的折扣。如果订单总金额等于或大于 70 美元且小于 150 美元,则应用 10%的折扣。如果订单总金额为 150 美元或更多,则客户将获得 20%的折扣。编写一个 Python 程序,提示用户输入订单总金额,然后计算并显示应用的折扣率、折扣金额(美元)和折扣后的最终金额。假设用户输入的金额为非负值。
解答
以下表格总结了提供的各种折扣。
| 范围 | 折扣 |
|---|---|
| amount < $30 | 0% |
| $30 ≤ amount < $70 | 5% |
| $70 ≤ amount < $150 | 10% |
| $150 ≤ amount | 20% |
Python 程序如下。
file_22.4-1a
amount = float(input("输入总金额: "))
if amount < 30:
discount = 0
elif amount >= 30 and amount < 70:
discount = 5
elif amount >= 70 and amount < 150:
discount = 10
elif amount >= 150:
discount = 20
discountAmount = amount * discount / 100
finalAmount = amount - discountAmount
print("您获得了", discount, "%的折扣", sep = "")
print("您节省了 $", discountAmount, sep = "")
print("您必须支付 $", finalAmount, sep = "")
然而,由于已知用户输入的是有效值而非负值,因此所有下划线的布尔表达式实际上并不需要。最终的 Python 程序如下,已移除所有不必要的评估。
file_22.4-1b
amount = float(input("输入总金额: "))
if amount < 30:
discount = 0
elif amount < 70:
discount = 5
elif amount < 150:
discount = 10
else:
discount = 20
discountAmount = amount * discount / 100
finalAmount = amount - discountAmount
print("您获得了", discount, "%的折扣", sep = "")
print("您节省了$", discountAmount, sep = "")
print("您必须支付$", finalAmount, sep = "")
练习 22.4-2 验证数据输入和计算折扣
将之前练习的 Python 程序重写为验证数据输入。当用户输入负值时,必须显示错误信息。
解决方案
解决这个练习的 Python 程序以一般形式给出如下。
主代码
amount = float(input("输入总金额: "))
if amount < 0:
print("输入的值是负数")
else:
代码片段 1:计算并显示应用的折扣率、折扣金额和折扣后的最终金额。
以下代码片段 1 来自之前的练习(练习 22.4-1)。它计算并显示应用的折扣率、折扣金额(美元)以及折扣后的最终金额。
代码片段 1
if amount < 30:
discount = 0
elif amount < 70:
discount = 5
elif amount < 150:
discount = 10
else:
discount = 20
discountAmount = amount * discount / 100
finalAmount = amount - discountAmount
print("您获得了", discount, "%的折扣", sep = "")
print("您节省了$", discountAmount, sep = "")
print("您必须支付$", finalAmount, sep = "")
在将代码片段 1 嵌入主代码后,最终的 Python 程序变为
file_22.4-2
amount = float(input("输入总金额: "))
if amount < 0:
print("输入的值是负数")
else:
if amount < 30: [更多…]
discount = 0
elif amount < 70:
discount = 5
elif amount < 150:
discount = 10
else:
discount = 20
discountAmount = amount * discount / 100
finalAmount = amount - discountAmount
print("您获得了", discount, "%的折扣", sep = "")
print("您节省了$", discountAmount, sep = "")
print("您必须支付$", finalAmount, sep = "")
练习 22.4-3 发送包裹
在邮局,发送中等包裹的运费取决于其重量以及目的地是否在国内。运费根据以下表格计算。
| 包裹重量(磅) | 国内目的地(每磅美元) | 国外目的地(美元) |
|---|---|---|
| weight ≤ 1 | $0.010 | $10 | |
| 1 < weight ≤ 2 | $0.013 | $20 | |
| 2 < weight ≤ 4 | $0.015 | $50 | |
| 4 < weight | $0.020 | $60 |
设计一个流程图并编写相应的 Python 程序,提示用户输入包裹的重量及其目的地(I:国内,O:国外)然后计算并显示运费。
解决方案
以下流程图以一般形式给出,解决了这个练习。

现在你需要两个多分支决策结构来计算国内和国内外的包裹的运费。这些分别显示在下方的左侧和右侧流程图片段中。

将这两个流程图与之前的流程图合并后,最终的流程图变为

相应的 Python 程序如下所示。
file_22.4-3
print("请输入重量和目的地:")
weight = float(input())
dest = input()
if dest.upper() == "I":
if weight <= 1: [更多…]
cost = weight * 0.010
elif weight <= 2:
cost = weight * 0.013
elif weight <= 4:
cost = weight * 0.015
else:
cost = weight * 0.020
else:
if weight <= 1: [更多…]
cost = 10
elif weight <= 2:
cost = 20
elif weight <= 4:
cost = 50
else:
cost = 60
print("运费:", cost)
用户可以以小写或大写形式输入字母 I(表示目的地)。upper() 方法确保程序在两种情况下都能正确执行。
练习 22.4-4 求解 y 的值
设计一个流程图并编写相应的 Python 程序,以找到并显示以下公式的 y 值(如果可能)。

解答
在这个练习中,对分数有两个限制:
►在分数
中,x 的值不能为 −1。
►在分数
中,x 的值不能为 +9。
对于所有其他分数,由于 x 所属的范围,分母不可能设置为 0。
Python 程序如下所示。
file_22.4-4a
x = float(input("请输入 x 的值:"))
if x > -5 and x <= 0:
if x != -1: [更多…]
y = x / (x - 3) + (8 + x) / (x + 1)
print(y)
else:
print("无效值")
elif x > 0 and x <= 6:
y = 40 * x / (x - 8) [更多…]
print(y)
elif x > 6 and x <= 20:
if x != 9: [更多…]
y = 3 * x / (x - 9)
print(y)
else:
print("无效值")
else:
y = abs(x) [更多…]
print(y)
如果你想知道是否可以移除所有的 print(y) 语句,并在程序末尾只保留一个 print(y) 语句,答案是“不可以”。由于存在不包含该语句的路径,你必须在每个必要的路径中都包含它。然而,通过稍微修改代码并在开始时检查无效值,你可以有机会将 print(y) 语句移动到所有路径的末尾。修改后的 Python 程序如下所示。
file_22.4-4b
x = float(input("请输入 x 的值:"))
if x == -1 or x == 9:
print("无效值")
else:
if x > -5 and x <= 0:
y = x / (x - 3) + (8 + x) / (x + 1)
elif x > 0 and x <= 6:
y = 40 * x / (x - 8)
elif x > 6 and x <= 20:
y = 3 * x / (x - 9)
else:
y = abs(x)
print(y)
现在,你可能想知道下划线的布尔表达式是否是多余的,对吧?假设你移除了它们,并且用户输入了 x 的值为-20。执行流程随后会到达布尔表达式 x <= 0,这将评估为 True。这意味着将计算
的分数而不是 x 的绝对值。
为了能够移除下划线的布尔表达式,你需要对代码进行轻微的修改。关键在于首先检查 x 的绝对值的情况。在此之后,你可以找到一个提出的解决方案。
file_22.4-4c
x = float(input("Enter a value for x: "))
if x == -1 or x == 9:
print("Invalid value")
else:
if x <= -5 or x > 20:
y = abs(x)
elif x <= 0:
y = x / (x - 3) + (8 + x) / (x + 1)
elif x <= 6:
y = 40 * x / (x - 8)
else:
y = 3 * x / (x - 9)
print(y)
显然,一个问题可以有多个解决方案。取决于你找到最优解!
练习 22.4-5 递增费率和电力消耗
LAV 电力公司根据以下表格(家庭账户的月度费率)向用户收取电费。假设所有额外费用,如传输服务费和分配费都已包括在内。
| 千瓦时(kWh) | 美元/千瓦时 |
|---|---|
| 千瓦时(kWh)≤ 500 | 美元/千瓦时 $0.10 |
| 501 ≤ kWh ≤ 2000 | 美元/千瓦时 $0.25 |
| 2001 ≤ kWh ≤ 4500 | 美元/千瓦时 $0.40 |
| 4501 ≤ kWh | 美元/千瓦时 $0.60 |
编写一个 Python 程序,提示用户输入消耗的总千瓦时数,然后计算并显示应付的总金额。
请注意,费率是递增的。
Solution
递增费率这个术语意味着当客户消耗,例如,2200 千瓦时时,并不是所有的千瓦时都按$0.40 收费。前 500 千瓦时按$0.10 收费,接下来的 1500 千瓦时按$0.25 收费,只有最后的 200 千瓦时按$0.40 收费。因此,客户必须支付
500 × $0.10 + 1500 × $0.25 + 200 × $0.40 = $505
应用相同的逻辑,当客户消耗,比如说,4800 千瓦时时,应付的总金额可以按以下方式计算。前 500 千瓦时按$0.10 收费,接下来的 1500 千瓦时按$0.25 收费,接下来的 2500 千瓦时按 0.40 收费,只有最后 300 千瓦时按$0.60 收费。因此,客户必须支付
500 × $0.10 + 1500 × $0.25 + 2500 × $0.40 + 300 × $0.60 = $1605
下面的图表可以帮助你完全理解如何在费率递增时计算应付的总金额。

这里展示了 Python 程序。
file_22.4-5
kwh = int(input("Enter number of Kilowatt-hours consumed: "))
if kwh <= 500:
t = kwh * 0.10
elif kwh <= 2000:
t = 500 * 0.10 + (kwh - 500) * 0.25
elif kwh <= 4500:
t = 500 * 0.10 + 1500 * 0.25 + (kwh - 2000) * 0.40
else:
t = 500 * 0.10 + 1500 * 0.25 + 2500 * 0.4 + (kwh - 4500) * 0.60
print("Total amount to pay:", t)
练习 22.4-6 累进费率和短信服务
LAV 手机公司每月向客户收取$8 的基本费率以发送短信。根据发送的短信总数,将收取额外费用,如下表所示。
| 发送短信数量 | 每条短信费用 |
|---|---|
| 50 以内 | 免费服务 |
| 51-150 | $0.05 |
| 151 及以上 | $0.10 |
联邦、州和地方税将使每张账单增加 10%。
编写一个 Python 程序,提示用户输入发送的短信数量,然后计算并显示总支付金额。
请注意,费率是累进的。
解决方案
Python 程序如下所示。
file_22.4-6
count = int(input("输入发送的短信数量:"))
if count <= 50:
extra = 0
elif count <= 150:
extra = (count - 50) * 0.05
else:
extra = 100 * 0.05 + (count - 150) * 0.10
totalWithoutTaxes = 8 + extra #添加基本费率$8
taxes = totalWithoutTaxes * 10 / 100 #计算总税费
total = totalWithoutTaxes + taxes #计算总支付金额
print("总支付金额:", total)
22.5 节 练习:带有决策控制结构的通用练习
练习 22.5-1 寻找闰年
编写一个 Python 程序,提示用户输入年份,然后显示消息指示它是否是闰年;如果不是,则显示消息“不是闰年”。此外,如果用户输入的年份小于 1582,必须显示错误消息。
(注意,这涉及到数据验证!)
解决方案
根据公历,公历首次于 1582 年引入,一个年份是闰年当且仅当以下条件之一满足:
第一个条件:年份能被 4 整除,但不能被 100 整除。
第二个条件:年份能被 400 整除。
在以下表中,一些年份不是闰年,因为两个条件都没有评估为真。
| 年份 | 闰年 | 条件 |
|---|---|---|
| 1600 | 是 | 第二个条件为真。它能被 400 整除 |
| 1900 | 否 | 两个条件均为假。 |
| 1904 | 是 | 第一个条件为真。它能被 4 整除,但不能被 100 整除 |
| 1905 | 否 | 两个条件均为假。 |
| 2000 | 是 | 第二个条件为真。它能被 400 整除 |
| 2002 | 否 | 两个条件均为假。 |
| 2004 | 是 | 第一个条件为真。它能被 4 整除,但不能被 100 整除 |
| 2024 | 是 | 第一个条件为真。它能被 4 整除,但不能被 100 整除 |
Python 程序如下所示。
file_22.5-1
y = int(input("输入年份:"))
if y < 1582:
print("错误!年份不能小于 1582")
else:
if y % 4 == 0 and y % 100 != 0 or y % 400 == 0:
print("闰年!")
else:
print("不是闰年")
与或运算符的优先级高于或运算符。
练习 22.5-2 显示月份中的天数
编写一个 Python 程序,提示用户输入一个年份和一个月份,然后显示该月有多少天。程序需要考虑闰年。如果是闰年,二月有 29 天而不是 28 天。此外,如果用户输入的年份小于 1582,必须显示错误消息。
解决方案
给定的以下 Python 程序以一般形式解决了这个练习。
主代码
y = int(input("Enter a year: "))
if y < 1582:
print("Error! The year cannot be less than 1582")
else:
m = int(input("Enter a month (1 - 12): "))
if m == 2:
代码片段 1:检查变量 y 表示的年份是否为闰年,并显示二月有多少天。
elif m in [4, 6, 9, 11]:
print("This month has 30 days")
else:
print("This month has 31 days")
代码片段 1,在此处显示,检查变量 y 表示的年份是否为闰年,并显示二月有多少天。
代码片段 1
if y % 4 == 0 and y % 100 != 0 or y % 400 == 0:
print("This month has 29 days")
else:
print("This month has 28 days ")
在将代码片段 1 嵌入主代码后,最终的 Python 程序变为
sentence = input("Enter a sentence: ")
y = int(input("Enter a year: "))
if y < 1582:
print("Error! The year cannot be less than 1582")
else:
m = int(input("Enter a month (1 - 12): "))
if m == 2:
if y % 4 == 0 and y % 100 != 0 or y % 400 == 0: [更多…]
print("This month has 29 days")
else:
print("This month has 28 days ")
elif m in [4, 6, 9, 11]:
print("This month has 30 days")
else:
print("This month has 31 days")
练习 22.5-3 检查正确的首字母大写和标点符号
编写一个 Python 程序,提示用户输入一个句子,然后检查其正确的首字母大写和标点符号。程序必须确定字符串是否以大写字母开头并以标点符号结尾(仅检查句号、问号和感叹号)。
解决方案
在这个练习中,你需要隔离字符串的第一个和最后一个字符。正如你所知,你可以使用子串表示法访问字符串中的任何单个字符。你可以使用索引 0 来访问第一个字符,索引 1 来访问第二个字符,依此类推。另一方面,你可以使用索引-1 来访问最后一个字符,索引-2 来访问倒数第二个字符,依此类推。
因此,你可以使用以下 Python 语句隔离字符串 sentence 的第一个字符
firstChar = sentence[0]
并且使用以下 Python 语句获取最后一个字符
lastChar = sentence[−1]
此处显示了 Python 程序。
file_22.5-3a
file_22.5-2
firstChar = sentence[0] #Get first character
lastChar = sentence[-1] #Get last character
sentenceIsOkay = True
if firstChar != firstChar.upper():
sentenceIsOkay = False
elif lastChar not in [".", "?", "!"]:
sentenceIsOkay = False
if sentenceIsOkay == True:
print("句子正确!")
在开始时,程序假设句子是正确的(sentenceIsOkay = True)。然后,它检查正确的首字母大写和正确的标点符号,如果发现有问题,它将 False 赋值给变量 sentenceIsOkay。
然而,另一种更 Pythonic 的方法如下所示。
file_22.5-3b
sentence = input("输入一个句子:")
if sentence[0] == sentence[0].upper() and sentence[-1] in [".", "?", "!"]:
print("句子正确!")
练习 22.5-4 判断数字是否为回文?
回文是一个在反转其位数后仍然相同的数字。例如,数字 13631 是一个回文。编写一个 Python 程序,让用户输入一个五位数整数,并测试这个数字是否是回文。此外,当用户输入浮点数或任何少于或超过五位数的整数时,必须显示不同类型的输入错误的不同错误消息。
(注意,这涉及到数据验证!)
解决方案
实际上存在三种不同的方法!第一种方法将数字的位数分成五个不同的变量,而第二种和第三种方法将数字作为字符串处理。让我们分析一下它们所有!
第一种方法
要测试用户是否输入了回文数,你需要将其位数分成五个不同的变量,就像你在第十三章中学到的那样。然后,你可以检查第一个数字是否等于第五个数字,第二个数字是否等于第四个数字。如果这个条件评估为 True,则该数字是回文。
为了验证数据输入,你需要检查用户是否输入了一个五位数。请记住,所有五位数都在 10000 到 99999 的范围内。因此,你只需将数据输入限制在这个范围内即可。
为了显示许多不同的错误消息,最佳实践是使用多重选择决策结构,首先检查所有情况下的数据输入有效性,然后尝试解决所需的问题。例如,如果你需要检查各种错误,你可以做如下操作。

以下是将 x 的位数分成 5 个不同变量的 Python 程序。
file_22.5-4a
x = float(input())
if x != int(x):
print("您输入了浮点数")
elif x < 10000:
print("您输入了少于五位数的数字")
elif x > 99999:
print("您输入了超过五位数的数字")
else:
将 x 的位数分成 5 个不同的变量
digit1, r = divmod(x, 10000)
digit2, r = divmod(r, 1000)
digit3, r = divmod(r, 100)
digit4, digit5 = divmod(r, 10)
if digit1 == digit5 and digit2 == digit4:
print("是回文")
else:
print("不是回文")
第二种方法
这种方法将数字视为字符串。它将第一个字符与最后一个字符进行比较,将第二个字符与倒数第二个字符进行比较。如果它们相等,则意味着该数字是回文。Python 程序如下所示。
file_22.5-4b
x = float(input())
if x != int(x):
print("你输入了一个浮点数")
elif x < 10000:
print("你输入的数字少于五位")
elif x > 99999:
print("你输入的数字超过五位")
else:
xStr = str(int(x))
if xStr[0] == xStr[-1] and xStr[1] == xStr[-2]:
print("回文")
else:
print("不是回文")
第三种方法
这种方法也将数字视为字符串。它使用切片将其反转,并将初始字符串与反转后的字符串进行比较。如果它们相等,则意味着该数字是回文。Python 程序如下所示。
file_22.5-4c
x = float(input())
if x != int(x):
print("你输入了一个浮点数")
elif x < 10000:
print("你输入的数字少于五位")
elif x > 99999:
print("你输入的数字超过五位")
else:
xStr = str(int(x))
xReversed = xStr[::-1]
if str(int(x)) == xReversed:
print("回文")
else:
print("不是回文")
22.6 布尔表达式参考和实用技巧
本节总结了你在使用 Python 的过程中遇到的所有布尔表达式,以及额外的表达式和有用的提示。无论你是初学者还是有经验的程序员,这些表达式和提示都将作为你编程工具箱中的宝贵工具。请保留它们,因为你不清楚何时会派上用场。对于其中的一些,提供了两种或多种实现所需结果的方法。
1)如何检查 x 中的数字是否在 A 和 B 之间
►x >= A and x <= B
►A <= x <= B
2)如何检查 x 中的数字是否不在 A 和 B 之间
►not(x >= A and x <= B)
►not(A <= x <= B)
►x < A or x > B
3)如何检查 x 是否等于 A、B 或 C
►x == A or x == B or x == C
►x in [A, B, C]
4)如何检查 x 是否既不等于 A,也不等于 B,也不等于 C
►x != A and x != B and x != C
►not(x == A or x == B or x == C)
►x not in [A, B, C]
5)如何检查 x 是否包含整数。请注意,变量 x 必须是浮点类型。
►x == int(x)
6)如何检查 x 是否包含浮点数。
►x != int(x)
7)如何检查 x 是否包含偶数。
►x % 2 == 0
►x % 2 != 1
►not(x % 2 == 1)
►not(x % 2 != 0)
8)如何检查 x 是否包含奇数。
►x % 2 == 1
►x % 2 != 0
►not(x % 2 == 0)
►not(x % 2 != 1)
9)如何检查 x 是否是 y 的整数倍
►x % y == 0
10)如何提取实数的整数部分
►x - int(x)
11)如何提取实数的第一个小数位
►int(x * 10) % 10
12)如何提取实数的第二个小数位
►int(x * 100) % 10
13)如何提取实数的第 N 位小数
►int(x * 10 ** N) % 10
14)如何提取整数的最后一位
►x % 10
15)如何提取一个整数的倒数第二位
►x // 10 % 10
16)如何提取一个整数的第 N 个到最后一个数字
►x // 10 ** N % 10
17)如何检查一个单词/句子是否以字母“B”开头
►x[0] == "B"
18)如何检查一个单词/句子是否以句号“.”结尾
►x[len(x) - 1] == "."
►x[-1] == "."
19)如何找出三个数字 x、y 和 z 中的中间数
►x + y + z - 最小值 - 最大值
20)如何找出三个数字 x、y 和 z 中最小的两个数的和
►x + y + z - 最大值
21)如何找出三个数字 x、y 和 z 中最大的两个数的和
►x + y + z - 最小值
22)如何找出五个数字 x、y、z、w 和 u 中三个中间数的和
►x + y + z + w + u – 最小值 - 最大值
23)如何检查两个数字之间的距离是否大于数字
►|x - y| > 数字
24)如何检查一个正整数是否有三位数
►x >= 100 且 x <= 999
►100 <= x <= 999
►len(str(x)) == 3
25)如何检查一个整数是否有三位数
►|x| >= 100 且 |x| <= 999
►100 <= |x| <= 999
►len(str(abs(x))) == 3
26)如何检查一个正整数有四位数字且以 5 开头
►x >= 5000 且 x <= 5999
27)如何检查两个数是否有相同的符号
►x > 0 且 y > 0 或 x < 0 且 y < 0
►x * y > 0
28)如何检查两个数要么都是偶数,要么都是奇数
►x % 2 == 0 且 y % 2 == 0 或 x % 2 == 1 且 y % 2 == 1
►x % 2 == y % 2
29)如何检查两个条件 BE1 或 BE2 中恰好有一个为真,但不是两个都为真(异或操作)
►BE1 and not(BE2) 或 BE2 and not(BE1)
30)如何检查 y 年是否为闰年
►y % 4 == 0 且 y % 100 != 0 或 y % 400 == 0
22.7 复习练习
完成以下练习。
1)编写一个 Python 程序,提示用户输入一个数值,然后计算并显示其平方根。此外,当用户输入负值时,必须显示错误消息。
2)设计一个流程图,允许用户输入一个整数,如果其最后一位数字等于 5,则显示消息“最后一位数字等于 5”;否则,显示消息“无特殊”。此外,如果用户输入负值,必须显示错误消息。
提示:您可以使用取模 10 操作来提取任何整数的最后一位数字。
3)设计一个流程图并编写相应的 Python 程序,允许用户输入两个整数,然后显示一个消息,指出至少有一个整数是奇数;否则,显示消息“无特殊”。此外,如果用户输入负值,必须显示错误消息。
4)设计一个流程图并编写相应的 Python 程序,提示用户输入一个整数,然后显示一个消息,指出该数是否为偶数;否则,显示“奇数”。此外,当用户输入负值或浮点数时,必须显示不同类型的输入错误消息。
5)设计一个流程图,并编写相应的 Python 程序,提示用户输入一个整数,然后显示一条消息,指出这个数是否可以被 3 和 4 整除;否则必须显示消息“NN 不是你要找的!”(其中 NN 是用户提供的数字)。例如,12 可以被 3 和 4 整除。此外,当用户输入负值或浮点数时,必须显示错误消息。
6)设计一个流程图,并编写相应的 Python 程序,允许用户输入两个整数,然后显示一条消息,指出这两个数是否都能被 3 和 4 整除;否则必须显示消息“X 和 Y 不是你要找的!”(其中 X 和 Y 是用户提供的数字)。此外,当用户输入负值或浮点数时,必须为每个整数显示不同类型的输入错误消息。
7)编写一个 Python 程序,显示以下菜单:
1)将开尔文温度转换为华氏温度
2)将华氏温度转换为开尔文
3)将华氏温度转换为摄氏温度
4)将摄氏温度转换为华氏温度
程序必须然后提示用户输入一个选择(1,2,3 或 4)和一个温度值,然后计算并显示所需值。此外,当用户输入的选择不是 1,2,3 或 4,或温度值低于绝对零时,必须显示不同类型的输入错误消息(绝对零的脚注^([17]))。
已知
1.8 × 开尔文 = 华氏温度 + 459.67
and
.
8)编写一个 Python 程序,模拟电子计算器的功能。程序必须首先提示用户输入一个整数,然后输入操作类型(+,−,*,/,DIV,MOD,POWER),最后输入第二个整数。随后,程序必须执行所选操作并显示结果。例如,如果用户输入的值是 13,+,和 2,程序必须显示以下消息:
13 + 2 的结果等于 15
程序应接受所有可能的操作类型,如“Div”,“DIV”,“div”,甚至“DiV”。在除以零的情况下,必须显示“无穷大”的消息。
9)将之前练习中的 Python 程序重写为验证数据输入。如果用户输入的不是+,−,*,/,DIV,MOD,POWER,必须显示错误消息。
10)编写一个 Python 程序,提示用户输入三个人的姓名和年龄,然后显示最年轻的人和最年长的人的姓名。
11)在一场歌唱比赛中,每位艺术家都会由五位评委根据他们的表演进行评分。然而,根据这场比赛的规则,总分是在排除最高分和最低分之后计算的。编写一个 Python 程序,提示用户输入艺术家的名字和每位评委给出的分数。然后程序必须显示艺术家的名字以及他们的总分。
12)编写一个 Python 程序,提示用户输入三个人的年龄,然后找出并显示中间年龄。
13)编写一个 Python 程序,提示用户输入三个人的名字和年龄,然后显示最年轻的人或最年长的人的名字,这取决于哪个人更接近中间的第三个年龄。
14)一个在线书店应用以下销售政策:购买 3 本书,支付最贵的两本书的费用。编写一个 Python 程序,允许用户输入三本书的价格和标题。然后必须显示客户需要支付的费用,以及免费提供的书的标题和价格。
15)设计一个流程图,并编写相应的 Python 程序,找出并显示以下公式中 y 的值(如果可能的话)。

16)设计一个流程图,并编写相应的 Python 程序,找出并显示以下公式中 y 的值(如果可能的话)。

17)重写练习 22.3-2 的 Python 程序,使用多分支决策结构。
提示:在外部双重分支决策结构中否定布尔表达式 x >= 0,并切换其两条路径。
18)编写一个 Python 程序,找出并显示以下公式中 y 的值(如果可能的话)。

19)一个正整数被称为阿姆斯特朗数,当其各位数字的立方和等于该数本身时。数字 371 就是这样,因为 3³ + 7³ + 1³ = 371。编写一个 Python 程序,允许用户输入一个三位整数,然后显示一个消息,指出用户提供的数字是否是阿姆斯特朗数。此外,当用户输入浮点数或不是三位数的任何数字时,必须显示不同类型的错误消息。
20)编写一个 Python 程序,提示用户输入一个日期(1 - 31),一个月份(1 - 12)和一个年份,然后找出并显示到该月结束还有多少天。程序必须考虑闰年。在闰年的情况下,二月有 29 天而不是 28 天。
21)编写一个 Python 程序,允许用户输入一个六个字母的单词,然后显示一个消息,指出是否每个第二个字母都是大写的。单词“AtHeNa”是这样的单词,但它也可以提供为“aThEnA”。
22)一个在线书店以每本 10 美元的价格出售电子书。根据以下表格提供数量折扣。
| 数量 | 折扣 |
|---|---|
| 3 ‐ 5 | 10% |
| 6 ‐ 9 | 15% |
| 10 ‐ 13 | 20% |
| 14 ‐ 19 | 27% |
| 20 或以上 | 30% |
编写一个 Python 程序,提示用户输入购买电子书的总数,然后显示折扣金额以及折扣后的总购买金额。假设用户输入的是有效值。
23)在超市,客户根据订单的税前金额获得的折扣如下表所示。
| 范围 | 折扣 |
|---|---|
| 金额 < $50 | 0% |
| $50 ≤ 金额 < $100 | 1% |
| $100 ≤ 金额 < $250 | 2% |
| $250 ≤ 金额 | 3% |
编写一个 Python 程序,提示用户输入订单的税前金额,然后计算并显示客户收到的折扣金额(如果有)。最后必须加上 19%的增值税(增值税)。此外,当用户输入负值时,必须显示错误信息。
24)身体质量指数(BMI)常用于确定成年人是否因身高而超重或体重不足。计算成年人 BMI 的公式是
.
编写一个 Python 程序,提示用户输入他们的年龄、体重(磅)和身高(英寸),然后根据以下表格显示描述。
| 身体质量指数 | 描述 |
|---|---|
| BMI < 15 | 非常严重体重不足 |
| 15.0 ≤ BMI < 16.0 | 严重体重不足 |
| 16.0 ≤ BMI < 18.5 | 体重不足 |
| 18.5 ≤ BMI < 25 | 正常 |
| 25.0 ≤ BMI < 30.0 | 超重 |
| 30.0 ≤ BMI < 35.0 | 严重超重 |
| 35.0 ≤ BMI | 非常严重超重 |
当用户输入的年龄小于 18 岁时,必须显示“无效年龄”的消息。
25)LAV 水公司根据以下表格(家庭账户的月度费率)向用户收取水费消耗费用。
| 水消耗(立方英尺) | 每立方英尺美元 |
|---|---|
| 消耗 ≤ 10 | $3 |
| 11 ≤ 消耗 ≤ 20 | $5 |
| 21 ≤ 消耗 ≤ 35 | $7 |
| 36 ≤ 消耗 | $9 |
编写一个 Python 程序,提示用户输入消耗的水量(立方英尺),然后计算并显示总付款金额。请注意,费率是累进制的。联邦、州和地方税将总计 10%的税加到每张账单上。此外,当用户输入负值时,必须显示错误信息。
26)编写一个 Python 程序,提示用户输入他们的应税收入和孩子的数量,然后根据以下表格计算应缴纳的总税额。但是,当用户至少有一个孩子时,总税额减少 2%。请注意,税率是累进制的。
| 应税收入(美元) | 税率 |
|---|---|
| 收入 ≤ 8000 | 10% |
| 8000 < 收入 ≤ 30000 | 15% |
| 30000 < 收入 ≤ 70000 | 25% |
| 70000 < 收入 | 30% |
27)蒲福风级是一种经验性的测量方法,它将风速与陆地或海洋上的观测条件联系起来。编写一个 Python 程序,提示用户输入风速,然后根据以下表格显示相应的蒲福风级和描述。当风速为 3 蒲福或更低时,必须显示附加信息“今天是垂钓日!!!”。此外,当用户输入负值时,必须显示错误信息。
| 风速(每小时英里) | 蒲福风级 | 描述 |
|---|---|---|
| 风速 < 1 | 0 | 无风 |
| 1 ≤ 风速 < 4 | 1 | 微风 |
| 4 ≤ 风速 < 8 | 2 | 微风 |
| 8 ≤ 风速 < 13 | 3 | 微风 |
| 13 ≤ 风速 < 18 | 4 | 中等风 |
| 18 ≤ 风速 < 25 | 5 | 清新风 |
| 25 ≤ 风速 < 31 | 6 | 强风 |
| 31 ≤ 风速 < 39 | 7 | 中等大风 |
| 39 ≤ 风速 < 47 | 8 | 大风 |
| 47 ≤ 风速 < 55 | 9 | 强风 |
| 55 ≤ 风速 < 64 | 10 | 暴风 |
| 64 ≤ 风速 < 74 | 11 | 狂风暴雨 |
| 74 ≤ 风速 | 12 | 飓风力量 |
第二十九章:在“决策控制结构”中复习
复习填字游戏
- 解决以下填字游戏。

横向
-
非(not)运算符也称为逻辑 _________。
-
这个布尔表达式可以由更简单的布尔表达式构建。
-
此运算符评估一个操作数是否存在于指定的序列中。
-
这个数字在反转其数字后保持不变。
-
或运算符也称为逻辑 _________。
-
一个正整数,其各位数字的立方和等于该数本身。
-
_______ 分支决策结构在两条路径上都包含一个语句或语句块。
纵向
-
与运算符也称为逻辑 _________。
-
此表显示了两个或多个布尔表达式之间逻辑运算的结果,以及它们所有可能值的组合。
-
(>)是一个 _____________ 运算符。
-
这是一个结果为真或假的值的表达式。
-
今年恰好能被 4 整除,但不能被 100 整除,或者恰好能被 400 整除。
-
此控制结构是嵌套在另一个结构中的结构。
-
这个数字被认为是一个偶数。
复习问题
回答以下问题。
-
什么是布尔表达式?
-
Python 支持哪些比较运算符?
-
哪个逻辑运算符执行逻辑合取?
-
哪个逻辑运算符执行逻辑析取?
-
逻辑运算符与何时返回 True 的结果?
-
逻辑运算符或何时返回 True 的结果?
-
说明逻辑运算符的优先级顺序。
-
说明算术、比较、成员和逻辑运算符的优先级顺序。
-
代码缩进是什么?
-
设计流程图并编写一个单分支决策结构的相应 Python 语句(一般形式)。描述这种决策结构是如何工作的。
-
设计流程图并编写一个双分支决策结构的相应 Python 语句(一般形式)。描述这种决策结构是如何工作的。
-
设计流程图并编写一个多分支决策结构的相应 Python 语句(一般形式)。描述这种决策结构是如何工作的。
-
“嵌套决策结构”这个术语是什么意思?
-
决策控制结构的嵌套可以有多深?有没有实际限制?
-
创建一个图,显示解决线性方程的所有可能路径。
-
创建一个图,显示解决二次方程的所有可能路径。
-
何时一年被认为是闰年?
-
什么是回文数?
第五部分
循环控制结构
第二十三章
循环控制结构简介
23.1 什么是循环控制结构?
循环控制结构是一种控制结构,它允许在满足指定条件之前多次执行语句或语句块。
23.2 从顺序控制到循环控制结构
下一个示例允许用户输入四个数字,然后计算并显示它们的总和。正如你所看到的,还没有使用循环控制结构,只有熟悉的顺序控制结构。
x = float(input())
y = float(input())
z = float(input())
w = float(input())
total = x + y + z + w
print(total)
虽然这段代码很短,但考虑一个类似的代码,它允许用户输入 1000 个数字而不是仅仅四个。你能想象不得不写 float(input())一千次输入语句吗?如果你能只写一次这个语句并指示计算机执行一千次,那会方便得多,不是吗?这就是循环控制结构发挥作用的地方!
在你深入研究循环控制结构之前,先尝试解决一个谜题!在不使用循环控制结构的情况下,尝试重写之前的示例,只使用两个变量 x 和 total。是的,你听对了!这段代码必须计算并显示四个用户提供的数字的总和,但必须只使用两个变量!你能找到方法吗?
嗯嗯……你现在在想什么很明显:“我可以用两个变量做的唯一一件事就是读取变量 x 中的一个值,然后将该值赋给变量 total”。你的想法相当正确,并且在这里进行了展示。
x = float(input()) # 读取第一个数字
total = x
这可以等价地写成
total = 0
x = float(input()) # 读取第一个数字
total = total + x
那接下来呢?现在,你可以做三件事,那就是:思考,思考,当然,还是思考!
第一个用户提供的数字已经被存储在变量 total 中,因此变量 x 现在可以用于进一步使用!因此,你可以重用变量 x 来读取第二个值,该值也将累积在变量 total 中,如下所示。
total = 0
x = float(input()) # 读取第一个数字
total = total + x
x = float(input()) # 读取第二个数字
total = total + x
语句 total = total + x 将 x 的值累加到 total 中,这意味着它将 x 的值加到 total 上,包括 total 中之前存在的任何值。例如,如果变量 total 包含值 5,而变量 x 包含值 3,则语句 total = total + x 将值 8 赋给变量 total。
由于第二个用户提供的数字已经累积在变量 total 中,变量 x 可以被重用!这个过程可以重复,直到所有四个数字都被读取并累积到变量 total 中。最终的代码如下。请注意,它还没有使用任何循环控制结构!
total = 0
x = float(input())
total = total + x
x = float(input())
total = total + x
x = float(input())
total = total + x
x = float(input())
total = total + x
print(total)
这两个代码和本节开头的一个初始代码被认为是等效的。然而,它们之间的主要区别在于,这个代码包含四对相同的语句。
显然,你可以使用这个例子来读取并找到超过四个数字的总和。然而,多次编写这些语句可能会相当繁琐,并且如果任何一对语句意外遗漏,可能会导致错误。
你真正需要的是保留一对语句,但使用循环控制结构执行四次(或者如果你愿意,甚至 1000 次)。你可以使用以下代码片段。
total = 0
execute_these_statements_4_times:
x = float(input())
total = total + x
print(total)
显然,Python 中没有 execute_these_statements_4_times 这样的语句。这只是为了演示目的,但很快你将了解 Python 支持的所有循环控制结构!
23.3 复习问题:正确/错误
对于以下每个陈述,选择正确或错误。
1)循环控制结构是一种允许在满足指定条件之前多次执行语句或语句块的构造。
2)可以使用一个序列控制结构提示用户输入 1000 个数字,然后计算它们的总和。
3)以下代码片段将值 10 累加到变量 total 中。
total = 10
a = 0
total = total + a
4)以下 Python 程序(不是代码片段)满足有效性的属性。
a = 5
total = total + a
print(total)
5)以下两个代码片段都将值 5 赋给变量 total。
a = 5
total = a
total = 0
a = 5
total = total + a
第二十四章
预测试、中测试和后测试循环结构
24.1 预测试循环结构
预测试循环结构如图所示。

让我们看看当执行流程达到预测试循环结构时会发生什么。如果布尔表达式评估为真,则执行该结构中的语句或语句块,并且执行流程会再次检查布尔表达式。如果布尔表达式再次评估为真,则过程会重复。当布尔表达式在某个时刻评估为假时,执行流程将退出循环。
决策符号(菱形或菱形)在决策控制结构和循环控制结构中都使用。然而,在循环控制结构中,菱形的一个出口总是向上方向。
“预测试循环结构”之所以被这样命名,是因为首先评估布尔表达式,然后执行该结构中的语句或语句块。
由于布尔表达式在进入循环之前被评估,预测试循环可能执行零次到多次迭代。
每次执行循环控制结构中的语句或语句块时,计算机科学中使用的术语是“循环迭代”或“循环执行迭代”。
Python 语句的一般形式是
while 布尔表达式:
一个语句或语句块
以下示例显示了从 1 到 10 的数字。
file_24.1
i = 1
while i <= 10:
打印(i)
i += 1
就像在决策控制结构中一样,循环控制结构内的语句必须缩进。
在 Python 中,while 循环也可以与 else 关键字结合使用,如下所示。
while 布尔表达式:
一个语句或语句块 1
else:
一个语句或语句块 2
然而,这个不寻常的特性在实践中很少使用,因此本书将不再进一步讨论。
练习 24.1-1 设计流程图并计算总迭代次数
设计以下代码片段的相应流程图。这段 Python 代码执行了多少次迭代?
i = 4
while i > 0:
i -= 1
打印("结束")
解答
相应的流程图片段如下。
.
接下来,一个跟踪表可以帮助你观察执行流程。
| 步骤 | 语句 | 备注 | i |
|---|---|---|---|
| 1 | i = 4 | 4 | |
| 2 | while i > 0: | 这评估为真 | 第 1 次 |
| 3 | i = i - 1 | 3 | |
| 4 | while i > 0: | 这评估为真 | 第 2 次 |
| 5 | i = i - 1 | 2 | |
| 6 | while i > 0: | 这评估为真 | 第 3 次 |
| 7 | i = i - 1 | 1 | |
| 8 | while i > 0: | 这评估为真 | 第 4 次 |
| 9 | i = i - 1 | 0 | |
| 10 | while i > 0: | 这评估为假 | |
| 11 | print("The end") | 它显示:The end |
如您从跟踪表中可以看出,总迭代次数是四次。
当预测试循环结构的语句或语句块执行 N 次时,布尔表达式将被评估 N+1 次。因此,为了确定总迭代次数,应计算语句或语句块执行的次数,而不是布尔表达式评估的次数。
练习 24.1-2 计算总迭代次数
这个 Python 程序执行了多少次迭代?
i = 4
while i >= 0:
print(i)
i -= 1
print("The end")
解决方案
这个练习几乎与上一个练习相同。主要区别在于这里的布尔表达式即使在 i = 0 时也保持为真,因此它执行了一个额外的迭代,即五次迭代。
练习 24.1-3 计算总迭代次数
这个 Python 程序执行了多少次迭代?
i = 1
while i != 6:
i += 2
print("The end")
解决方案
让我们创建一个跟踪表来观察执行流程。
| 步骤 | 语句 | 备注 | i |
|---|---|---|---|
| 1 | i = 1 | 1 | |
| 2 | while i != 6: | 这将评估为真 | 1^(st) |
| 3 | i += 2 | 3 | |
| 4 | while i != 6: | 这将评估为真 | 2^(nd) |
| 5 | i += 2 | 5 | |
| 6 | while i != 6: | 这将评估为真 | 3^(rd) |
| 7 | i += 2 | 7 | |
| 8 | while i != 6: | 这将评估为真 | … |
| 9 | … | … |
如您从跟踪表中可以看出,由于变量 i 从未被赋予值 6,这个代码片段将无限次迭代!显然,这个代码不满足有限性的属性。
练习 24.1-4 计算总迭代次数
这个 Python 程序执行了多少次迭代?
i = -10
while i > 0:
print(i)
i -= 1
print("The end")
解决方案
初始时,变量 i 被赋予值-10。布尔表达式直接评估为假,执行流程直接跳转到 print("The end")语句。因此,这个代码片段执行了零次迭代。
练习 24.1-5 找到四个数字的和
使用预测试循环结构,编写一个 Python 程序,允许用户输入四个数字,然后计算并显示它们的和。
解决方案
你还记得第 23.2 节中计算四个数字之和的例子吗?在稍作工作后,提出的代码片段变成了
total = 0
execute_these_statements_4_times:
x = float(input())
total = total + x
print(total)
现在,你需要一种方法来“呈现”execute_these_statements_4_times 语句的真实 Python 语句。while 语句可以实现这一点,但你需要一个额外的变量来计算总迭代次数。这样,当执行了所需的迭代次数后,执行流程将退出循环。
以下是一个通用的代码片段,它根据指定的总迭代次数进行迭代,
i = 1
while i <= total_number_of_iterations:
一个语句或语句块
i += 1
其中 total_number_of_iterations 可以是常量值,也可以是变量或表达式。
将此代码片段与前面的代码片段合并后,最终程序变为
file_24.1-5
total = 0
i = 1
while i <= 4:
x = float(input()) [更多…]
total = total + x
i += 1
print(total)
变量 i 的名称不是绑定性的。你可以使用任何你想要的变量名,例如 counter、count、k 等。
练习 24.1-6 求奇数和
设计一个流程图并编写相应的 Python 程序,允许用户输入 20 个整数,然后计算并显示奇数的和。
解答
这相当简单。程序在循环中必须执行的操作是检查用户提供的数字是否为奇数,如果是,则该数字必须累加到变量 total 中;偶数必须忽略。流程图如下。它包括一个嵌套在预测试循环结构中的单分支决策结构。

相应的 Python 程序如下。
file_24.1-6
total = 0
i = 1
while i <= 20:
x = int(input())
if x % 2 != 0:
total += x # 这等价于 total = total + x
i += 1
print(total)
只要保持它们在语法和逻辑上的正确性,你可以在任何循环控制结构内部嵌套任何决策控制结构。
练习 24.1-7 求 N 个数字的和
编写一个 Python 程序,允许用户输入 N 个数字,然后计算并显示它们的和。N 的值必须在程序开始时由用户提供。
解答
在这个练习中,总迭代次数取决于用户必须输入的值。以下是一个通用的代码片段,它迭代 N 次,其中 N 由用户提供。
n = int(input())
i = 1
while i <= n:
一个语句或语句块
i += 1
根据你所学到的知识,最终程序如下
file_24.1-7
total = 0
n = int(input())
i = 1
while i <= n:
x = float(input())
total += x
i += 1
print(total)
练习 24.1-8 求未知数量数字的和
编写一个 Python 程序,允许用户重复输入整数值,直到输入值是-1。当数据输入完成后,必须显示输入数字的总和。(-1 的值不得包含在最终的总和中)。接下来,创建一个跟踪表来检查你的程序是否使用 10、20、5 和-1 作为输入值正确运行。
解答
在这个练习中,总迭代次数是未知的。如果你使用决策控制结构,你的程序将类似于下面的代码片段。
total = 0
x = int(input())
if x != -1: #检查变量 x [更多…]
total += x #并执行此语句
x = int(input()) #并执行此语句
if x != -1: #检查变量 x
total += x #并执行此语句
x = int(input()) #并执行此语句
if x != -1: #检查变量 x
total += x #并执行此语句
x = int(input()) #并执行此语句
…
…
print(total)
现在我们将使用循环控制结构重写此程序。最终的程序如下所示。如果您尝试跟随执行流程,您会发现它与之前的程序等效。
file_24.1-8
total = 0
x = int(input())
while x != -1: #检查变量 x
total += x #并执行此语句
x = int(input()) #并执行此语句
print(total)
现在我们创建一个跟踪表,以确定使用 10、20、5 和 −1 作为输入值时,此程序是否正常工作。
| 步骤 | 语句 | 备注 | x | total |
|---|---|---|---|---|
| 1 | total = 0 | ? | 0 | |
| 2 | x = int(input()) | 10 | 0 | |
| 3 | while x != ‑1: | 这将评估为 True | ||
| 4 | total += x | 10 | 10 | |
| 5 | x = int(input()) | 20 | 10 | |
| 6 | while x != ‑1: | 这将评估为 True | ||
| 7 | total += x | 20 | 30 | |
| 8 | x = int(input()) | 5 | 30 | |
| 9 | while x != ‑1: | 这将评估为 True | ||
| 10 | total += x | 5 | 35 | |
| 11 | x = int(input()) | −1 | 35 | |
| 12 | while x != −1: | 这将评估为 False | ||
| 13 | print(total) | 它显示:35 |
如您所见,最终,变量 total 包含的值是 35,这确实是 10 + 20 + 5 的和。此外,最终用户提供的值 −1 并未参与最终的总和。
当在循环开始迭代之前已知迭代次数时,循环通常被称为“确定循环”。然而,在这个练习中,在循环开始迭代之前并不知道迭代次数,它取决于某个条件。这种类型的循环通常被称为“不确定循环”。
练习 24.1-9 求解 20 个数的乘积
编写一个 Python 程序,让用户输入 20 个数字,然后计算并显示它们的乘积。
解答
如果您要使用一个序列控制结构,它将类似于以下代码片段。
p = 1
x = float(input()) [更多…]
p = p * x
x = float(input())
p = p * x
…
…
x = float(input())
p = p * x
注意变量 p 被初始化为 1 而不是 0。这是为了使语句 p = p * x 正确运行;否则最终乘积将是零。
使用之前练习中的知识,最终程序如下所示
file_24.1-9
p = 1
i = 1
当 i <= 20 时:
x = float(input())
p = p * x
i += 1
print(p)
24.2 后测试循环结构
后测试循环结构在以下流程图中显示。
.
在循环控制结构中,菱形的一个出口总是向上方向。
让我们看看当执行流程达到后测试循环结构时会发生什么。结构中的语句或语句块直接执行,如果布尔表达式评估为 False,则执行流程返回到结构语句或语句块之上的点。语句或语句块再次执行,如果布尔表达式再次评估为 False,则过程重复。当布尔表达式在某个时刻评估为 True 时,执行流程退出循环。
后测试循环与先测试循环的不同之处在于,首先执行结构中的语句或语句块,然后评估布尔表达式。因此,后测试循环至少执行一次迭代!
每次执行循环控制结构的语句或语句块时,计算机科学中使用的术语是“循环正在迭代”或“循环执行迭代”。
尽管大多数计算机语言(如 C、C++、C#、Java、PHP 和 Visual Basic 等)直接支持后测试循环结构,但不幸的是,Python 并不支持这种结构,因为没有直接的表达式。所以现在的问题是:“如果你仍然想在 Python 程序中使用后测试循环结构,你能做什么?”
在 Python 中,您仍然可以使用 while 语句以及 if 和 break 语句间接地编写后测试循环。主要思想是创建一个无限循环(也称为无限循环),当结构语句块末尾的布尔表达式评估为 True 时跳出循环。这个想法在以下代码片段中给出,以一般形式表示。
while True:
一个语句或语句块
if 布尔表达式: break
您可以使用 break 语句在循环实际完成所有迭代之前跳出循环。
以下示例显示从 1 到 10 的数字。
file_24.2
i = 1
while True:
print(i)
i += 1
if i > 10: break
练习 24.2-1 设计流程图和计算总迭代次数
设计以下 Python 程序的相应流程图。这个 Python 程序执行了多少次迭代?
i = 3
while True:
i = i - 1
if i <= 0: break
print("结束")
解决方案
对应的流程图如下。
.
现在,让我们创建一个跟踪表来观察执行流程。
| 步骤 | 语句 | 备注 | i |
|---|---|---|---|
| 1 | i = 3 | 3 | |
| 2 | i = i - 1 | 2 | |
| 3 | if i <= 0: break | 这评估为 False | |
| 4 | i = i - 1 | 1 | |
| 5 | if i <= 0: break | 这评估为 False | |
| 6 | i = i - 1 | 0 | |
| 7 | if i <= 0: break | 这将评估为 True | |
| 8 | 打印("结束") | 它显示:结束 |
如从跟踪表所示,总迭代次数为三次。
后测试循环结构的语句或语句块执行 N 次,布尔表达式评估 N 次。因此,要确定总迭代次数,可以计算语句或语句块执行的次数,或者布尔表达式评估的次数。这两个计数是相等的!
练习 24.2-2 计算总迭代次数
这个 Python 程序执行了多少次迭代?
i = 3
while True:
打印(i)
i -= 1
if i < 0: break
打印("结束")
解答
这个练习几乎与上一个练习相同。主要区别在于这里的布尔表达式即使在 i = 0 时也保持为 False,因此它执行了一个额外的迭代,即四次迭代。
练习 24.2-3 设计流程图和计算总迭代次数
设计以下代码片段对应的流程图。这段代码执行了多少次迭代?
i = -1
while True:
打印("你好!")
i -= 1
if i <= 0: break
打印("结束")
解答
对应的流程图如下。
.
初始时,变量 i 被赋予值-1。在循环内部,显示消息“你好!”,并将变量 i 减一(结果为-2)。布尔表达式 i ≤ 0 评估为 True,执行流程直接跳转到 Write ("The end")语句。因此,这个算法执行了一次迭代!
练习 24.2-4 计算总迭代次数
这个代码片段程序执行了多少次迭代?
i = 1
while True:
i = i + 2
if i == 4: break
打印("结束")
解答
让我们创建一个跟踪表来观察执行流程。
| 步骤 | 语句 | 备注 | i |
|---|---|---|---|
| 1 | i = 1 | 1 | |
| 2 | i = i + 2 | 3 | |
| 3 | if i == 4: break | 这将评估为 False | |
| 4 | i = i + 2 | 5 | |
| 5 | if i == 4: break | 这将评估为 False | |
| 6 | i = i + 2 | 7 | |
| 7 | if i == 4: break | 这将评估为 False | |
| 8 | … | … | … |
| 9 | … | … |
如从跟踪表所示,由于从未将值 4 赋给变量 i,这个代码片段将无限次迭代!显然,这个代码不满足有限性的属性。
练习 24.2-5 求 N 个数的乘积
编写一个 Python 程序,允许用户输入 N 个数字,然后计算并显示它们的乘积。N 的值必须在程序开始时由用户提供。如果你将后测试循环结构与先测试循环结构交换,会发生什么?对于 N 的所有可能的输入值,这两个程序是否以完全相同的方式运行?
解答
以下两个程序都允许用户输入 N 个数字,计算并显示它们的乘积。左边的一个使用预测试,而右边的一个使用后测试循环结构。如果您尝试执行它们并输入 N 的任何大于零的值,这两个程序将完全以相同的方式运行!
file_24.2-5a
n = int(input())
p = i = 1
while i <= n:
x = float(input())
p = p * x
i += 1
print(p)
file_24.2-5b
n = int(input())
p = i = 1
while True:
x = float(input())
p = p * x
i += 1
if i > n: break
print(p)
然而,当用户为 N 输入非正值^([18])时,这两个 Python 程序的操作方式却不同。例如,如果输入值为 0,则左边的程序执行零次迭代,而右边的程序执行一次迭代。显然,左边的程序是解决这个练习的正确选择!
与后测试循环结构不同,预测试循环结构可能执行零次迭代!
24.3 中测试循环结构
中测试循环结构在以下流程图中显示。

让我们看看当执行流程达到中测试循环结构时会发生什么。结构中的语句或语句块 1 直接执行,如果布尔表达式评估为假,则执行语句或语句块 2,执行流程回到结构中语句或语句块 1 的上方。语句或语句块 1 再次执行,如果布尔表达式再次评估为假,则过程重复。当布尔表达式在某个时刻评估为真时,执行流程退出循环。
尽管这种循环控制结构在某些计算机语言(如 Ada)中得到直接支持,但不幸的是,Python 并不支持。然而,您仍然可以使用 while 语句以及 if 和 break 语句来编写中测试循环。主要思想是创建一个无限循环,并在两个语句(或语句块)之间的布尔表达式评估为真时跳出循环。这种思想在以下一般形式的代码片段中给出。
while True:
语句或语句块 1
if Boolean_Expression: break
语句或语句块 2
您可以使用 break 语句在循环实际完成所有迭代之前跳出循环。
以下示例显示数字 1 到 10。
file_24.3
i = 1
while True:
print(i)
if i >= 10: break
i += 1
练习 24.3-1 设计流程图和计算迭代总数
设计以下代码片段对应的流程图,并创建一个跟踪表以确定变量 i 在每一步的值。
i = 10
while True:
print(i)
i += 5
if i > 45: break
print(i ** 2)
i += 10
print("The end")
解决方案
相应的流程图片段如下。

现在,让我们创建一个跟踪表来观察执行流程。
| 步骤 | 语句 | 备注 | i |
|---|---|---|---|
| 1 | i = 10 | 10 | |
| 2 | print(i) | 它显示:10 | |
| 3 | i += 5 | 15 | |
| 4 | if i > 45: break | 这评估为 False | |
| 5 | print(i ** 2) | 它显示:225 | |
| 6 | i += 10 | 25 | |
| 7 | print(i) | 它显示:25 | |
| 8 | i += 5 | 30 | |
| 9 | if i > 45: break | 这评估为 False | |
| 10 | print(i ** 2) | 它显示:900 | |
| 11 | i += 10 | 40 | |
| 12 | print(i) | 它显示:40 | |
| 13 | i += 5 | 45 | |
| 14 | if i > 45: break | 这评估为 False | |
| 15 | print(i ** 2) | 它显示:2025 | |
| 16 | i += 10 | 55 | |
| 17 | print(i) | 它显示:55 | |
| 18 | i += 5 | 60 | |
| 19 | if i > 45: break | 这评估为 True | |
| 20 | print("The end") | 它显示:The end |
24.4 复习问题:真/假
对以下每个陈述选择真或假。
1)前测试循环可能执行零次迭代。
2)在流程图中,前测试循环结构中菱形符号的两个出口都向上。
3)前测试循环结构的语句或语句块至少执行一次。
4)while 语句在其布尔表达式评估为 True 时停止迭代
5)在前测试循环结构中,当结构中的语句或语句块执行 N 次时,布尔表达式被评估 N-1 次。
6)后测试循环可能执行零次迭代。
7)在后测试循环结构中,当结构中的语句或语句块执行 N 次时,其布尔表达式也被评估 N 次。
8)您不能在后测试循环结构内部嵌套决策控制结构。
9)在中测试循环结构中,语句或语句块 1 被执行的次数与语句或语句块 2 相同。
10)在以下代码片段中,单词“Hello”被显示 10 次。
i = 1
while i <= 10:
print("Hello")
i += 1
11)以下 Python 程序不满足有限性的属性。
i = 1
while i != 10:
print("Hello")
i += 2
12)在以下代码片段中,单词“Hello”被显示无限次。
i = 1
while True:
print("Hello")
if i < 10: break
13)以下 Python 程序(不是代码片段)满足有效性的属性。
while True:
print("Hello")
i -= 2
if i <= 10: break
14)以下 Python 程序不满足确定性的属性。
b = int(input())
if b != 1:
while True:
a = 1 / (b - 1)
b += 1
if b > 10: break
15)在以下代码片段中,单词“Zeus”被显示 10 次。
i = 1
while True:
print("Zeus")
if i > 10: break
i += 1
24.5 复习问题:多项选择题
选择以下每个陈述的正确答案。
1)在流程图中,菱形符号正在被使用
a)在决策控制结构中。
b)在循环控制结构中。
c)所有以上选项
2)后测试循环结构
a)比预测试循环结构多执行一次迭代。
b)与预测试循环结构的迭代次数相同。
c)取决于
3)在后测试循环结构中,结构的语句或语句块
a)在循环的布尔表达式评估之前执行。
b)在循环的布尔表达式评估后执行。
c)以上都不是
4)在以下代码片段
i = 1
while i < 10:
打印("Hello Hermes")
i += 1
显示消息“Hello Hermes”
a)10 次。
b)9 次。
c)1 次。
d)0 次。
e)以上都不是
5)在以下代码片段
i = 1
while i < 10:
打印("Hi!")
打印("Hello Ares")
i += 1
显示消息“Hello Ares”
a)10 次。
b)11 次。
c)1 次。
d)0 次。
e)以上都不是
6)在以下代码片段
i = 1
while i < 10:
i += 1
打印("Hi!")
打印("Hello Aphrodite")
显示消息“Hello Aphrodite”
a)10 次。
b)1 次。
c)0 次。
d)以上都不是
7)在以下代码片段
i = 1
while i >= 10:
打印("Hi!")
打印("Hello Apollo")
i += 1
显示消息“Hello Apollo”
a)10 次。
b)1 次。
c)0 次。
d)以上都不是
8)以下 Python 程序
n = int(input())
s = 0
i = 1
while i < n:
a = float(input())
s = s + a
i += 1
打印(s)
计算并显示总和
a)与变量 n 的值表示的数字一样多。
b)与表达式 n - 1 的结果表示的数字一样多。
c)与变量 i 的值表示的数字一样多。
d)以上都不是
9)在以下代码片段
i = 1
while True:
打印("Hello Poseidon")
i += 1
if i <= 5: break
显示消息“Hello Poseidon”
a)5 次。
b)1 次。
c)0 次。
d)以上都不是
10)在以下代码片段
i = 1
while True:
打印("Hello Athena")
i += 5
if i == 50: break
显示消息“Hello Athena”
a)至少一次。
b)至少 10 次。
c)无限次数。
d)所有以上选项
11)在以下代码片段
i = 0
while True:
打印("Hello Apollo")
if i <= 10: break
显示消息“Hello Apollo”
a)至少一次。
b)无限次数。
c)以上都不是
12)在以下代码片段
i = 10
while True:
i -= 1
if i > 0: break
打印("Hello Aphrodite")
显示消息“Hello Aphrodite”
a)至少一次。
b)无限次数。
c)十次
d)以上都不是
24.6 复习练习
完成以下练习。
1)识别以下 Python 程序中的错误(错误)。它必须显示数字 3、2、1 和消息“The end”。
i = 3
while True
打印(i)
i -= 1
if i < 0 break
打印(The end)
2)创建一个跟踪表以确定下一个 Python 程序每一步中变量的值。这个 Python 程序执行了多少次迭代?
i = 3
x = 0
while i >= 0:
i -= 1
x += i
打印(x)
3)设计相应的流程图并创建一个跟踪表以确定下一个 Python 程序中每个步骤中变量的值。这个 Python 程序执行了多少次迭代?
i = -5
while i < 10:
i -= 1
print(i)
4)创建一个跟踪表以确定下一个 Python 程序中每个步骤中变量的值。这个 Python 程序执行了多少次迭代?
a = 2
while a <= 10:
b = a + 1
c = b * 2
d = c - b + 1
if d == 4:
print(b, ",", c)
elif d == 5:
print(c)
elif d == 8:
print(a, ",", b)
else:
print(a, ",", b, ",", d)
a += 4
5)创建一个跟踪表以确定下一个 Python 程序中每个步骤中变量的值。这个 Python 程序执行了多少次迭代?
a = 1
b = 1
c = 0
d = 0
while b < 2:
x = a + b
if x % 2 != 0:
c = c + 1
else:
d = d + 1
a = b
b = c
c = d
6)在以下代码片段中填入空缺,以便所有循环都恰好执行四次迭代。
i)
a = 3
while a > …… :
print(a)
a -= 1
ii)
a = 5
while a < …… :
print(a)
a += 1
iii)
a = 9
while a != 10:
print(a)
a = a + ……
iv)
a = 1
while a != …… :
print(a)
a -= 2
v)
a = 2
while a < …… :
print(a)
a = 2 * a
vi)
a = 1
while a < …… :
print(a)
a = a + 0.1
7)创建一个跟踪表以确定下一个 Python 程序中每个步骤中变量的值。这个 Python 程序执行了多少次迭代?
y = 5
x = 38
while True:
y *= 2
x += 1
print(y)
if y >= x: break
8)创建一个跟踪表以确定下一个 Python 程序中每个步骤中变量的值。这个 Python 程序执行了多少次迭代?
x = 1
while True:
if x % 2 == 0:
x += 1
else:
x += 3
print(x)
if x >= 12: break
9)创建一个跟踪表以确定下一个 Python 程序中每个步骤中变量的值。这个 Python 程序执行了多少次迭代?
y = 2
x = 0
while True:
y = y ** 2
if x < 256:
x = x + y
print(x, ",", y)
if y >= 65535: break
10)创建一个跟踪表以确定下一个 Python 程序中每个步骤中变量的值。这个 Python 程序执行了多少次迭代?
a = 2
b = 4
c = 0
d = 0
while True:
x = a + b
if x % 2 != 0:
c = c + 5
elif d % 2 == 0:
d = d + 5
else:
c = c + 3
a = b
b = d
if c >= 11: break
11)在以下代码片段中填入空缺,以便所有循环都恰好执行六次迭代。
i)
a = 5
while True:
print(a)
a -= 1
if a <= …… : break
ii)
a = 12
while True:
print(a)
a += 1
if a >= …… : break
iii)
a = 20
while True:
print(a)
a = a + ……
if a == 23: break
iv)
a = 100
while True:
print(a)
a -= 20
if a == …… : break
v)
a = 2
while True:
print(a)
a = 2 * a
if a == …… : break
vi)
a = 10
while True:
print(a)
a = a + 0.25
if a > …… : break
12)在以下代码片段中填入空缺,以便所有代码在最后都显示值 10。
i)
x = 0
y = 0
while True:
x += 1
y += 2
if x > …… : break
print(y)
ii)
x = 1
y = 20
while True:
x -= 1
y -= 2.5
if x < …… : break
print(y)
iii)
x = 3
y = 2.5
while True:
x -= 1
y *= 2
if x < …… : break
print(y)
iv)
x = 30
y = 101532
while True:
x -= ……
y = y // 10
if x < 0: break
print(y)
13)使用预测试循环结构,编写一个 Python 程序,让用户输入 N 个数字,然后计算并显示它们的总和和平均值。N 的值必须在程序开始时由用户提供。
14)使用预测试循环结构,编写一个 Python 程序,让用户输入 N 个整数,然后计算并显示偶数的乘积。N 的值必须在程序开始时由用户提供。此外,如果用户提供的所有整数都是奇数,则必须显示消息“你没有输入任何偶数整数”。
15)使用预测试循环结构,编写一个 Python 程序,让用户输入 100 个整数,然后计算并显示以 0 结尾的整数的总和。例如,10、2130 和 500 都是这样的数。
提示:您可以使用取模 10 操作来隔离任何整数的最后一位。
16)使用预测试循环结构,编写一个 Python 程序,让用户输入 20 个整数,然后计算并显示由三位数组成的总和。
提示:所有三位整数都在 100 到 999 之间。
17)使用预测试循环结构,编写一个 Python 程序,让用户重复输入数值,直到输入值为 0。当数据输入完成后,必须显示输入的数的乘积。(最后输入的 0 必须不包括在最终乘积中)。接下来,创建一个跟踪表来检查程序是否使用 3、2、9 和 0 作为输入值正确运行。
18)一个城镇的人口现在为 30000,每年增长率为 3%。使用预测试循环结构,编写一个 Python 程序来确定人口超过 100000 需要多少年。
19)使用后测试循环结构,设计一个流程图并编写相应的 Python 程序,让用户输入 50 个整数,然后计算并显示奇数的总和和偶数的总和。
20)使用后测试循环结构,编写一个 Python 程序,让用户输入 N 个整数,然后计算并显示负数的乘积。N 的值必须在程序开始时由用户提供,并且最终乘积必须始终以正值显示。假设用户为 N 输入的值大于 0。
21)使用后测试循环结构,编写一个 Python 程序,提示用户输入五个整数,然后计算并显示所有以 5 开头的三位整数的乘积。例如,512、555 和 593 都是这样的数。
提示:所有以 5 开头的三位整数都在 500 到 599 之间。
- 蜂箱当前的人口为 50,000。每年,蜂箱因新生而经历 5%的增长,但也因环境原因面临 15%的死亡率。使用后测试循环结构,编写一个 Python 程序来确定人口下降到 20,000 以下需要多少年。
第二十五章
确定循环
25.1 for 语句
在第二十四章中,你肯定注意到了,while 语句被用来迭代已知次数和未知次数(在循环开始迭代时迭代次数未知的情况下)。换句话说,while 语句被用来创建确定性和非确定性循环。
由于确定循环在计算机编程中非常常用,几乎每种计算机语言,包括 Python,都包含一个比 while 语句更易读、更方便的特殊语句——这就是 for 语句。
for 语句的一般形式是
for element in sequence:
一个语句或语句块
其中 element 是一个变量,它被分配给序列的每个连续值,并且结构语句或语句块对每个值执行一次。
Python 的 for 语句的流程图如下所示。

然而,你会发现,这本书使用了一个简化的流程图,如下所示。

下面的示例显示了数字 1, 2, 3, 4 和 5。
file_25.1a
for i in [1, 2, 3, 4, 5]:
print(i)
它对应的流程图如下所示
.
下面的示例显示了字母“H”,“e”,“l”,“l”,“o”(所有都不带双引号)。
file_25.1b
for letter in "Hello":
print(letter)
它对应的流程图如下所示
.
如你在第 11.2 节中学到的,Python 的 range()函数可以用来创建整数序列。你可以使用此函数与 for 语句一起使用,以扩展 for 语句的可能性,如下所示
for counter in range([初始值,] 最终值 [, 步长]):
一个语句或语句块
其中
►counter 是一个整型变量。
►初始值是序列的起始值。此参数是可选的。如果省略,其默认值是 0。
►序列直到但不包括最终值。
►步长是序列中每个数字之间的差值。此参数是可选的。如果省略,其默认值是 1。
初始值、最终值和步长参数必须是整数。也允许使用负值。
下面的示例显示了数字 0 到 10。
file_25.1c
for i in range(0, 11, 1):
print(i)
当步长为 1 时,可以省略第三个参数。上一个示例也可以写成
file_25.1d
for i in range(0, 11):
print(i)
此外,当初始值为 0 时,可以省略第一个参数。上一个示例也可以写成
file_25.1e
for i in range(11):
print(i)
下一个示例显示了数字 2, 4, 6, 8 和 10。
file_25.1f
for i in range(2, 12, 2):
print(i)
以下示例显示从-2 到-10 的偶数,使用变量而不是常量值作为初始值、最终值和步长。
file_25.1g
x1 = -2
x2 = -12
t = -2
for i in range(x1, x2, t):
print(i)
永远不要在循环内部更改计数器(此处为 i)的值!同样适用于初始值(此处为 x1)、最终值(此处为 x2)和步长(此处为 t)。这会使你的代码难以阅读,并可能导致结果错误。尽管如此,如果你坚持这样做,请使用 while 语句。
以下示例显示字母“H”,“e”,“l”,“l”,“o”(所有都不带双引号)。
file_25.1h
message = "Hello"
for i in range(len(message)):
print(message[i])
len()函数返回变量 message 包含的字符数,而 message[i]表示法返回字符串变量 message 中指定位置(由变量 i 指示)的字符(参见第 14.3 节)。
就像 Python 的 while 语句一样,for 语句也可以与 else 关键字结合使用,如下所示。
for element in sequence:
一个语句或语句块 1
else:
一个语句或语句块 2
然而,这个不寻常的特性在实践中很少使用,所以本书中不会进一步讨论。
练习 25.1-1 创建跟踪表
创建一个跟踪表以确定在输入值 1 时,下一个 Python 程序每一步中变量的值。
a = int(input())
for i in range(-3, 5, 2):
a = a * 3
print(i, a)
解答
range()函数返回序列-3, -1, 1, 3。for 循环将-3, -1, 1, 3 的值分别赋给变量 i,每个迭代一个值。相应的跟踪表如下所示。
| 步骤 | 语句 | 备注 | a | i |
|---|---|---|---|---|
| 1 | a = int(input()) | 1 | ? | |
| 2 | i = −3 | 1 | −3 | |
| 3 | a = a * 3 | 3 | −3 | |
| 4 | i = −1 | 3 | −1 | |
| 5 | a = a * 3 | 9 | −1 | |
| 6 | i = 1 | 9 | 1 | |
| 7 | a = a * 3 | 27 | 1 | |
| 8 | i = 3 | 27 | 3 | |
| 9 | a = a * 3 | 81 | 3 | |
| 10 | print(i, a) | 它显示:3 81 |
练习 25.1-2 创建跟踪表
创建一个跟踪表以确定在输入值 4 时,下一个 Python 程序每一步中变量的值。
a = int(input())
for i in range(6, a - 1, -1):
print(i)
解答
range()函数返回序列 6, 5, 4。以下是用以确定每一步中变量值的跟踪表。
| 步骤 | 语句 | 备注 | a | i |
|---|---|---|---|---|
| 1 | a = int(input()) | 4 | ? | |
| 2 | i = 6 | 4 | 6 | |
| 3 | print(i) | 它显示:6 | ||
| 4 | i = 5 | 4 | 5 | |
| 5 | print(i) | 它显示:5 | ||
| 6 | i = 4 | 4 | 4 | |
| 7 | print(i) | 它显示:4 |
练习 25.1-3 计算迭代总数
计算以下代码片段在两次不同执行中的迭代总数。
两次执行的输入值分别为:(i) 6,和(ii) 5。
n = int(input())
for i in range(5, n + 1):
print(i)
解答
对于输入值 6,range()函数返回一个包含数字 5 和 6 的序列。因此,循环执行了两次迭代。
相应地,对于输入值 5,循环显然只执行了一次迭代。
练习 25.1-4 找出四个数的和
编写一个 Python 程序,提示用户输入四个数,然后计算并显示它们的和。
解答
在练习 24.1-5 中,使用 while 语句提出的解决方案如下:
total = 0
i = 1
while i <= 4:
x = float(input())
total = total + x
i += 1
print(total)
现在用 for 语句重写这个程序非常容易,并在每次数据输入之前显示提示信息。
file_25.1-4
total = 0
for i in range(4):
x = float(input("Enter a number: "))
total = total + x
print(total)
注意循环控制结构内部缺少 i += 1 语句。在 for 语句中,计数器(此处变量 i)在每次循环迭代开始时自动更新。
练习 25.1-5 从 0 到 N 寻找平方根
编写一个 Python 程序,提示用户输入一个整数,然后计算并显示从 0 到该用户提供的整数的所有整数的平方根。
解答
这个练习很简单。用户输入一个整数,程序根据该整数迭代相应的次数。Python 程序如下。
file_25.1-5
from math import sqrt
n = int(input("Enter an integer: "))
for i in range(n + 1):
print(sqrt(i))
练习 25.1-6 找出 1 + 2 + 3 + … + 100 的和
编写一个 Python 程序,计算并显示以下求和:
S = 1 + 2 + 3 + … + 100
解答
如果使用序列控制结构来解决这个问题,代码片段可能如下所示。
s = 0
i = 1
s = s + i
i = 2
s = s + i
i = 3
s = s + i
…
…
i = 100
s = s + i
让我们使用跟踪表来更好地理解它。
| 步骤 | 语句 | 备注 | i | s |
|---|---|---|---|---|
| 1 | s = 0 | 0 | ? | 0 |
| 2 | i = 1 | 1 | 0 | |
| 3 | s = s + i | 0 + 1 = 1 | 1 | 1 |
| 4 | i = 2 | 2 | 1 | |
| 5 | s = s + i | 0 + 1 + 2 = 3 | 2 | 3 |
| 6 | i = 3 | 3 | 3 | |
| 7 | s = s + i | 0 +1 + 2 + 3 = 6 | 3 | 6 |
| 8 | i = 4 | 4 | 6 | |
| ... | … | … | … | |
| ... | … | … | … | |
| 199 | i = 99 | 99 | 4851 | |
| 200 | s = s + i | 99 | 4950 | |
| 201 | i = 100 | 100 | 4950 | |
| 202 | s = s + i | 0 + 1 + 2 + 3 + …+ 99 + 100 = 5050 | 100 | 5050 |
现在一切都已清楚,你可以这样做,这次使用 for 循环,在每次迭代中,将 1 到 100 的值赋给变量 i。
file_25.1-6
s = 0
for i in range(1, 101):
s = s + i
print(s)
练习 25.1-7 求 2 × 4 × 6 × 8 × 10 的乘积
编写一个 Python 程序,计算并显示以下乘积:
P = 2 × 4 × 6 × 8 × 10
解答
让我们使用以下序列控制结构来解决这个问题。变量 p 必须初始化为 1 而不是 0。这是为了确保 p = p * i 语句能够正确执行;否则,最终乘积将为零。
p = 1
i = 2
p = p * i
i = 4
p = p * i
i = 6
p = p * i
i = 8
p = p * i
i = 10
p = p * i
如前一个练习(练习 25.1-6)中所述,这个序列控制结构可以用 for 循环替换,如下所示。
file_25.1-7
p = 1
for i in range(2, 12, 2):
p = p * i
print(p)
练习 25.1-8 求 2² + 4² + 6² + … (2N)² 的和
编写一个 Python 程序,让用户输入一个整数 N,然后计算并显示以下求和:
S = 2² + 4² + 6² + … (2N)²
解答
在这个练习中,for 循环必须在每次迭代中将值 2、4、6、… 2N 分配给变量 i,并且这些值在累加到变量 s 之前必须被提升到平方。以下是一个最终的 Python 程序。
file_25.1-8
N = int(input())
s = 0
for i in range(2, 2 * N + 2, 2):
s = s + i ** 2
print(s)
练习 25.1-9 求 3³ + 6⁶ + 9⁹ + … (3N)^(3N) 的和
编写一个 Python 程序,让用户输入一个整数 N,然后计算并显示以下求和:
S = 3³ + 6⁶ + 9⁹ + …+ (3N)^(3N)
解答
这与上一个练习非常相似。唯一的区别是变量 i 在累加到变量 s 之前必须被提升到 i 次幂。使用 for 循环,最终的 Python 程序如下。
file_25.1-9
N = int(input())
s = 0
for i in range(3, 3 * N + 3, 3):
s = s + i ** i
print(s)
练习 25.1-10 求正数的平均值
编写一个 Python 程序,让用户输入 100 个数字,然后计算并显示正数的平均值。添加所有必要的检查,以确保程序满足确定性属性。
解答
由于你知道总的迭代次数,你可以使用一个 for 循环。然而,在循环内部,决策控制结构必须检查用户提供的数字是否为正数;如果是,它必须将用户提供的数字累加到变量 s 中。变量 count 计算输入的正数数量。当执行流程退出循环时,可以计算出平均值。以下是一个 Python 程序。
file_25.1-10
s = 0
count = 0
for i in range(100):
x = float(input())
if x > 0:
s = s + x
count += 1
if count != 0:
print(s / count)
else:
print("没有输入正数!")
if count != 0 语句是必要的,因为有可能用户只会输入负值(或零)。通过包含这个检查,程序防止了任何除以零的错误,并因此满足了确定性的属性。
练习 25.1-11 计算元音字母
编写一个 Python 程序,提示用户输入一条消息,然后计算并显示该消息中包含的元音字母数量。
解决方案
以下 Python 程序计算英文消息中的元音字母。
file_25.1-11
message = input("输入一条英文消息:")
vowels = "AEIOU"
count = 0
for character in message:
if character.upper() in vowels:
count += 1
print("元音字母:", count)
注意这里操作符 in 的两种用法。在第一种情况下,它用于确定迭代次数,而在第二种情况下,它用于检查字母是否存在于字符串 vowels 中(见第 15.5 节)。
25.2 适用于 for 循环的规则
在编写带有 for 循环的程序时,你必须始终遵循某些规则,因为它们可以让你免受不良副作用的影响。
►规则 1:var/计数器变量可以出现在 for 循环内的语句中,但它们的值永远不应该被更改。如果它们是变量而不是常量值,则同样适用于 final_value 和 step。
►规则 2:步长永远不能为零。如果设置为零,Python 会抛出错误!
►规则 3:如果初始值小于最终值,则步长必须是正的。如果它是负的,循环执行零次迭代。以下示例在屏幕上什么也不打印
for i in range(5, 9, -1):
file_25.2-1
然而,请记住,故意违反此规则在某些情况下可能是有用的。
►规则 4:如果初始值大于最终值,则步长必须是负的。如果它是正的,循环执行零次迭代。以下示例也在屏幕上什么也不打印
for i in range(10, 6):
print(i)
然而,请记住,故意违反此规则在某些情况下可能是有用的。
练习 25.2-1 求 N 个数的和
编写一个 Python 程序,提示用户输入 N 个数字,然后计算并显示它们的和。N 的值必须在程序开始时由用户提供。
解决方案
解决方案在此处展示。
total += a # 这相当于 total = total + a
n = int(input("输入要输入的数字数量:"))
total = 0
for i in range(n):
a = float(input("输入第" + str(i + 1) + "个数字:"))
print(i)
print("和:", total)
Even though it violates the fourth rule of for-loops, in this particular exercise this situation is very useful. If the user enters a non-positive value for variable n, the for statement performs zero iterations.
25.3 复习问题:对/错
对以下每个陈述选择对或错。
1)在 for 语句中,变量 element 在每次循环开始时自动分配序列的连续值。
2)当迭代次数已知时可以使用确定循环。
3)在确定循环中,循环的语句或语句块至少执行一次。
4)在 range()函数中,初始值不能大于最终值。
5)当执行流程退出 for 循环时,计数器的值不等于 final_value。
6)在 range()函数中,初始值、最终值和步长不能是浮点数。
7)在带有 range()函数的 for 语句中,当步长设置为 0 时,循环执行 0 次迭代。
8)在 for 语句中,计数变量可以出现在循环内的语句中,但其值不应被更改。
9)在带有 range()函数的 for 语句中,步长在特定情况下可以是 0。
10)在以下代码片段中,单词“Hello”被显示 10 次。
for i in range(1, 10):
打印("Hello")
11)以下代码片段总是可以执行的。
b = int(input())
for i in range(0, 9, b):
打印("Hello")
12)以下代码片段满足确定性的属性。
from math import sqrt
for i in range(-10, 10):
打印(sqrt(i))
25.4 复习问题:多项选择题
选择以下每个陈述的正确答案。
1)使用 for 语句的确定循环
a)执行比等效的先测试循环结构(使用 while 语句)多一次迭代。
b)执行比等效的先测试循环结构(使用 while 语句)少一次迭代。
c)以上都不是
2)可以使用 for 语句的确定循环来解决以下问题
a)用户反复输入数字,直到输入的值是-1。
b)用户反复输入数字,直到输入的值大于 final_value。
c)所有选项
d)以上都不是
3)在 for 循环中,初始值、最终值和步长可以是
a)一个常数。
b)一个变量。
c)一个表达式。
d)以上所有选项
4)在 for 循环中,当 final_value 和 step 是变量时,它们的值
a)在循环内不能更改。
b)在循环内不应更改。
c)以上都不是
5)在 for 循环中,当计数器增加时,步长是
a)大于零。
b)等于零。
c)小于零。
d)以上都不是
6)在 for 循环中,计数器的初始值
a)必须是 0。
b)可以是 0。
c)不能是负数。
d)以上都不是
7)在 for 循环中,变量 counter 自动分配序列的连续值
a)在每次迭代开始时。
b)在每次迭代结束时。
c)不是自动分配的。
d)以上都不是
8)在以下代码片段
i = 1
for i in range(5, 6):
打印("Hello Hera")
显示消息“Hello Hera”
a)5 次。
b)1 次。
c)0 次。
d)以上都不是
9)在以下代码片段
for i in range(5, 5):
i = 1
打印("Hello Artemis")
显示消息“Hello Artemis”
a)1 次。
b)无限次.
c)0 次.
d)以上皆非
10)在以下代码片段中
for i in range(5, 6):
i = 6
print("Hello Ares")
显示的消息是“Hello Ares”
a)无限次.
b)1 次.
c)0 次.
d)以上皆非
11)在以下代码片段中
for i in range(2, 9):
if i % 2 == 0:
print("Hello Demeter")
显示的消息是“Hello Demeter”
a)8 次.
b)7 次.
c)5 次.
d)以上皆非
12)在以下代码片段中
for i in range(40, 51):
print("Hello Dionysus")
显示的消息是“Hello Dionysus”
a)1 次.
b)2 次.
c)10 次.
d)11 次.
13)在以下代码片段中
k = 0
for i in range(1, 7, 2):
k = k + i
print(k)
显示的值是
a)3.
b)6.
c)9.
d)以上皆非
14)在以下代码片段中
k = 0
for i in range(10, -15, -5):
k = k + i
print(i)
显示的值是
a)0
b)−15.
c)−10.
d)以上皆非
25.5 复习练习
完成以下练习。
1)创建一个跟踪表以确定下一个 Python 程序每一步中变量的值。这个 Python 程序执行了多少次迭代?
a = 0
b = 0
for j in range(0, 10, 2):
if j < 5:
b += 1
else:
a += j - 1
print(a, ",", b)
2)创建一个跟踪表以确定下一个 Python 程序对于两个不同执行的每一步中变量的值。
两次执行的输入值分别是:(i) 10,和(ii) 21。
a = int(input())
b = a
for j in range(a - 5, a + 1, 2):
if j % 2 != 0:
b = a + j + 5
else:
b = a - j
print(b)
3)创建一个跟踪表以确定下一个 Python 程序对于输入值 12 的每一步中变量的值。
a = int(input())
for j in range(2, a, 3):
x = j * 3 + 3
y = j * 2 + 10
if y - x > 0 or x > 30:
y *= 2
x += 4
print(x, ",", y)
4)填写以下代码片段中的空白,以便所有循环都恰好执行五次迭代。
i)
for a in range(5, …… + 1):
print(b)
b += a
ii)
for a in range(0, …… + 1, 5):
print(b)
b += a
iii)
for a in range(……, -16, -2):
print(b)
b += a
iv)
for a in range(-11, -16, ……):
print(b)
b += a
5)不使用跟踪表,你能找出以下 Python 程序将显示什么?
word = "Zeus"
i = 1
s = ""
for letter in word:
s = s + i * letter
i += 1
print(s)
6)设计一个流程图并编写相应的 Python 程序,提示用户输入 20 个数,然后计算并显示它们的乘积和平均值。
7)编写一个 Python 程序,计算并显示从 0 到 360°的正弦值,步长为 0.5。已知 2π = 360°。
8)编写一个 Python 程序,提示用户输入一个度数,然后计算并显示从 0 到用户提供的数的余弦值,步长为 1。已知 2π = 360°。
9)编写一个 Python 程序,计算并显示以下数的和:
S = 1 + 3 + 5 + … + 99
- 编写一个 Python 程序,允许用户输入一个整数 N,然后计算并显示以下乘积:
P = 2¹ × 4³ × 6⁵ × … × 2N^((2N−1))
- 编写一个 Python 程序,计算并显示以下数列的和:
S = 1 + 2 + 4 + 7 + 11 + 16 + 22 + 29 + 37 + … + 191
-
设计一个流程图并编写相应的 Python 程序,允许教师输入学生的总数以及他们的成绩,然后计算并显示那些获得“A”(即 90 到 100 分)的平均值。添加所有必要的检查,以确保程序满足确定性的属性。
-
设计一个流程图并编写相应的 Python 程序,提示用户输入 30 个四位数整数,然后计算并显示那些首位为 5 且末位为 3 的整数的总和。例如,值 5003、5923 和 5553 都是此类整数。
-
设计一个流程图并编写相应的 Python 程序,提示用户输入 N 个整数,然后显示其中偶数的总数。程序开始时必须由用户提供 N 的值。此外,如果用户提供的所有整数都是奇数,则必须显示消息“您没有输入任何偶数整数”。
-
设计一个流程图并编写相应的 Python 程序,提示用户输入 50 个整数,然后计算并显示奇数的平均值和偶数的平均值。
-
设计一个流程图并编写相应的 Python 程序,提示用户将两个整数分别输入到变量 start 和 finish 中,然后显示从 start 到 finish 的所有整数。然而,在程序开始时,必须检查变量 start 是否大于变量 finish。如果发生这种情况,程序必须交换它们的值,以确保它们始终处于正确的顺序。
-
设计一个流程图并编写相应的 Python 程序,提示用户将两个整数分别输入到变量 start 和 finish 中,然后显示从 start 到 finish 的所有是 5 的倍数的整数。然而,在程序开始时,必须检查变量 start 是否大于变量 finish。如果发生这种情况,程序必须交换它们的值,以确保它们始终处于正确的顺序。
-
编写一个 Python 程序,提示用户输入一个实数和一个整数,然后显示第一个数以第二个数为指数的结果,而不使用指数运算符(**)或 Python 的内置 pow() 函数。
-
编写一个 Python 程序,提示用户输入一条消息,然后显示它包含的单词数量。例如,如果输入的字符串是“我的名字是比尔·博拉斯”,程序必须显示“输入的消息包含 5 个单词”。假设单词由单个空格字符分隔。
提示:使用 len() 函数获取用户提供的消息包含的字符数。
-
编写一个 Python 程序,提示用户输入一条消息,然后显示每个单词的平均字母数。例如,如果输入的消息是“我的名字是阿芙罗狄忒·博拉”,程序必须显示“每个单词的平均字母数是 4.4”。空格字符不计入。
-
编写一个 Python 程序,提示用户输入一条消息,然后计算并显示该消息中包含的辅音字母数量。
-
编写一个 Python 程序,提示用户输入一条消息,然后计算并显示该消息中包含的元音字母数量、辅音字母数量以及算术字符数量。
第二十六章
嵌套循环控制结构
26.1 什么是嵌套循环?
嵌套循环是指在另一个循环内部的循环,换句话说,是外部循环内部的内部循环。
外部循环控制内部循环的完整迭代次数。这意味着外部循环的第一个迭代触发内部循环开始迭代直到完成。然后,外部循环的第二个迭代再次触发内部循环开始迭代直到完成。这个过程重复,直到外部循环完成所有迭代。
以以下 Python 程序为例。
file_26.1
for i in range(1, 3):
for j in range(1, 4): [更多…]
print(i, j)
在这个程序中,由变量 i 控制的循环是外部循环,它决定了内部循环完成的完整迭代次数。具体来说,当变量 i 为 1 时,内部循环执行三次迭代(对于 j = 1, j = 2 和 j = 3)。完成内部循环后,外部循环需要再执行一次迭代(对于 i = 2)。因此,内部循环重新开始,再次执行三次新的迭代(对于 j = 1, j = 2 和 j = 3)。
之前的例子类似于以下例子。
i = 1 #外部循环将值 1 赋予变量 i
for j in range(1, 4): #并且内部循环重新开始并执行三次新的迭代
print(i, j)
i = 2 #外部循环将值 2 赋予变量 i
for j in range(1, 4): #并且内部循环重新开始并执行三次新的迭代
print(i, j)
输出结果如下。

只要不违反语法规则,你可以嵌套任意多的循环控制结构。然而,出于实际考虑,当你达到四或五层嵌套时,整个结构会变得非常复杂且难以理解。然而,经验表明,你作为程序员一生中可能最多会做到三到四层嵌套。
内部和外部循环不必是同一类型。例如,一个 for 语句可以嵌套(包含)一个 while 语句,反之亦然。
练习 26.1-1 说“Hello Zeus”。计算总迭代次数。
找出消息“Hello Zeus”显示的次数。
file_26.1-1
for i in range(3):
for j in range(4):
print("Hello Zeus")
Solution
变量 i 和 j 的值(按出现顺序)如下:
►对于 i = 0,内部循环执行 4 次迭代(对于 j = 0, j = 1, j = 2 和 j = 3),并且消息“Hello Zeus”显示 4 次。
►对于 i = 1,内部循环执行 4 次迭代(对于 j = 0, j = 1, j = 2 和 j = 3),并且消息“Hello Zeus”显示 4 次。
►对于 i = 2,内部循环执行 4 次迭代(对于 j = 0, j = 1, j = 2 和 j = 3),并且消息“Hello Zeus”显示 4 次。
因此,消息“Hello Zeus”总共显示 3 × 4 = 12 次。
外循环控制内循环的完整迭代次数!
练习 26.1-2 创建跟踪表
对于下一个代码片段,确定变量 a 在程序结束时的值。
a = 1
i = 5
while i < 7:
for j in range(1, 5, 2):
a = a * j + i
i += 1
print(a)
解答
这里显示了跟踪表。
| 步骤 | 语句 | 备注 | a | i | j |
|---|---|---|---|---|---|
| 1 | a = 1 | 1 | ? | ? | |
| 2 | i = 5 | 1 | 5 | ? | |
| 3 | while i < 7 | 这评估为 True | |||
| 4 | j = 1 | 1 | 5 | 1 | |
| 5 | a = a * j + i | 6 | 5 | 1 | |
| 6 | j = 3 | 6 | 5 | 3 | |
| 7 | a = a * j + i | 23 | 5 | 3 | |
| 8 | i += 1 | 23 | 6 | 3 | |
| 9 | while i < 7 | 这评估为 True | |||
| 10 | j = 1 | 23 | 6 | 1 | |
| 11 | a = a * j + i | 29 | 6 | 1 | |
| 12 | j = 3 | 29 | 6 | 3 | |
| 13 | a = a * j + i | 93 | 6 | 3 | |
| 14 | i += 1 | 93 | 7 | 3 | |
| 15 | while i < 7 | 这评估为 False | |||
| 16 | print(a) | 它显示:93 |
程序结束时,变量 a 包含的值是 93。
26.2 适用于嵌套循环的规则
除了适用于 for 循环的四个规则(在第 25.2 节中介绍)之外,在编写嵌套循环程序时,你还应该始终遵循两个额外的规则,因为它们可以让你避免不希望的结果。
►规则 1:内循环必须完全开始和结束在外循环内,这意味着循环不得重叠。
►规则 2:外循环和内(嵌套)循环不得使用相同的变量。
练习 26.2-1 违反第一规则
设计一个违反嵌套循环第一规则的流程图片段,该规则指出,“内循环必须完全开始和结束在外循环内”。
解答
以下流程图片段违反了嵌套循环的第一规则。
.
如果你尝试跟随执行流程,你会注意到它平稳地执行了 5 × 10 = 50 次迭代。没有人能看出这个流程图是错误的。实际上,它在技术上是对的。然而,问题在于它的可读性。要辨别这个流程图旨在完成什么非常困难。此外,这种结构与你已经学过的任何已知的循环控制结构都不匹配,因此不能直接转换为 Python 程序。尽量避免这种嵌套循环!
练习 26.2-2 违反第二规则
找出消息“Hello”显示的次数。
i = 1
while i <= 3:
for i in range(4, -1, -1):
print("Hello")
i += 1
解答
初看之下,人们可能会认为单词“Hello”显示 3 × 5 = 15 次。然而,仔细观察后会发现事情并不总是像看起来那样。这个程序违反了嵌套循环的第二规则,即“外循环和内(嵌套)循环不得使用相同的变量”。让我们设计相应的流程图。

如果你尝试跟随此流程图片段的执行流程,你可以看到当内层循环完成所有五次迭代后,变量 i 包含的值为 0。然后,变量 i 增加 1,外层循环再次重复。这个过程可以无限进行,因为变量 i 永远不会超过外层循环布尔表达式所需的值 3。因此,“Hello”消息无限次显示。
26.3 复习问题:判断对错
对以下每个陈述选择正确或错误。
1)嵌套循环是外层循环中的一个内层循环。
2)可以在预测试循环结构中嵌套中间测试循环结构。
3)循环控制结构中嵌套的最大层数是四层。
4)当两个循环控制结构嵌套在一起时,最后开始的循环必须先完成。
5)当两个循环控制结构嵌套在一起时,它们必须不使用相同的计数器变量。
6)在以下代码片段中,单词“Hello”显示六次。
for i in range(1, 4):
for j in range(1, 4):
print("Hello")
7)在以下代码片段中,单词“Hello”显示 12 次。
for i in range(2):
for j in range(1, 4):
for k in range(1, 5, 2):
print("Hello")
8)在以下代码片段中,单词“Hello”无限次显示。
i = 1
while i <= 4:
for i in range(3, 0, -1):
print("Hello")
i += 1
9)在以下代码片段中,单词“Hello”显示九次。
for i in range(3):
j = 1
while True:
print("Hello")
j += 1
if j >= 4: break
10)在以下程序中至少有一个中间测试循环结构。
s = 0
while not False:
while not False:
a = int(input())
if a >= -1: break
if a == -1: break
s += a
print(s)
26.4 复习问题:多项选择题
对以下每个陈述选择正确答案。
1)在以下代码片段中
for i in range(1, 3):
for j in range(1, 3):
print("Hello")
变量 i 和 j 的值(按出现顺序)是
a)j = 1, i = 1, j = 1, i = 2, j = 2, i = 1, j = 2, i = 2
b)i = 1, j = 1, i = 1, j = 2, i = 2, j = 1, i = 2, j = 2
c)i = 1, j = 1, i = 2, j = 2
d)j = 1, i = 1, j = 2, i = 2
2)在以下代码片段中
x = 2
while x > -2:
while True:
x -= 1
print("Hello Hestia")
if x >= -2: break
显示消息“Hello Hestia”
a)4 次。
b)无限次数。
c)0 次。
d)以上皆非
3)在以下代码片段中
x = 1
while x != 500:
for i in range(x, 4):
print("Hello Artemis")
x += 1
显示消息“Hello Artemis”
a)无限次数。
b)1500 次。
c)6 次。
d)以上皆非
4)以下代码片段
for i in range(1, 4):
for j in range(1, i + 1):
print(i * j, ", ", sep = "", end = "")
print("The End!")
显示
a)1, 2, 4, 3, 6, 9, The End!
b)1, 2, 3, 4, 6, 9, The End!
c)1, 2, The End!, 4, 3, The End!, 6, 9, The End!
d)以上皆非
5)以下代码片段
i = 1
while i <= 10:
for i in range(10, 0, -1):
print("Hello Dionysus")
i += 1
不满足该属性。
a)definiteness.
b)finiteness.
c)effectiveness.
26.5 复习练习
完成以下练习。
1)填写以下代码片段中的空白,以便所有代码片段都显示“Hello Hephaestus”消息 100 次。
i)
for a in range(6, ……):
for b in range(25):
print("Hello Hephaestus")
ii)
for a in range(0, …… + 1, 5):
for b in range(10, 20):
print("Hello Hephaestus")
iii)
for a in range(……, -17, -2):
for b in range(150, 50, -5):
print("Hello Hephaestus")
iv)
for a in range(-11, -16, -1):
for b in range(100, …… + 1, 2):
print("Hello Hephaestus")
2)设计相应的流程图,并创建一个跟踪表以确定在下一个代码片段的每个步骤中变量的值。
a = 1
j = 1
while j <= 2:
i = 10
while i < 30:
a = a + j + i
i += 10
j += 0.5
print(a)
3)创建一个跟踪表,以确定在下一个代码片段的每个步骤中变量的值。s = s + i * j 语句执行了多少次?
s = 0
for i in range(1, 5):
for j in range(3, i - 1, -1):
s = s + i * j
print(s)
4)创建一个跟踪表,以确定在下一个 Python 程序的每个步骤中变量的值。这个 Python 程序执行了多少次迭代?
三次执行中的输入值分别为:(i) NO, (ii) YES, NO; 和 (iii) YES, YES, NO.
s = 1
y = 25
while True:
for i in range(1, 4):
s = s + y
y -= 5
ans = input()
if ans != "YES": break
print(s)
5)编写一个 Python 程序以显示以下形式的时和分表。
0 0
0 1
0 2
0 3
...
0 59
1 0
1 1
1 2
...
23 59
请注意,输出是对齐的。
6)使用嵌套循环控制结构,编写一个 Python 程序以显示以下输出。
5 5 5 5 5
4 4 4 4
3 3 3
2 2
1
7)使用嵌套循环控制结构,编写一个 Python 程序以显示以下输出。
0
0 1
0 1 2
0 1 2 3
0 1 2 3 4
0 1 2 3 4 5
8)使用嵌套循环控制结构,编写一个 Python 程序以显示以下矩形。
-
* * * * * * * * *
-
* * * * * * * * *
-
* * * * * * * * *
-
* * * * * * * * *
然后尝试在不使用任何循环控制结构的情况下完成同样的任务!
9)编写一个 Python 程序,提示用户输入一个介于 3 和 20 之间的整数 N,然后显示每边大小为 N 的正方形。例如,如果用户输入 4 作为 N,程序必须显示以下正方形。
-
* * *
-
* * *
-
* * *
-
* * *
然后尝试在不使用任何循环控制结构的情况下完成同样的任务!
10)编写一个 Python 程序,提示用户输入一个介于 3 和 20 之间的整数 N,然后显示每边大小为 N 的空心正方形。例如,如果用户输入 4 作为 N,程序必须显示以下空心正方形。
-
* * *
-
*
-
*
-
* * *
然后尝试在不使用任何循环控制结构的情况下完成同样的任务!
11)使用嵌套循环控制结构,编写一个 Python 程序,显示以下三角形。
-
*
-
* *
-
* * *
-
* * * *
-
* * *
-
* *
-
*
然后,尝试只用一个 for 循环(不允许使用嵌套循环控制结构!)
第二十七章
更多关于带有循环控制结构的流程图
27.1 简介
通过学习前面的章节,你已经熟悉了所有的循环控制结构。由于流程图是学习“算法思维”和帮助你更好地理解特定控制结构的理想方式,本章将教你如何将 Python 程序转换为流程图,以及如何将流程图转换为 Python 程序。
27.2 将 Python 程序转换为流程图
要将 Python 程序转换为流程图,你需要回忆所有循环控制结构及其相应的流程图。以下将总结它们。
前测试循环结构
while Boolean_Expression:
一个或多个语句

测试后循环结构
while True:
一个或多个语句
if Boolean_Expression: break

中间测试循环结构
while True:
一个或多个语句 1
if Boolean_Expression: break
一个或多个语句 2

For 循环
for element in sequence:
一个或多个语句

接下来,你将找到许多练习,可以帮助你澄清你可能仍然需要帮助理解的事情。
练习 27.2-1 设计流程图
设计与以下 Python 程序相对应的流程图。
i = 50
while i > 10:
if i % 2 == 1:
print(i)
i -= 5
解决方案
这个 Python 程序包含一个前测试循环结构,嵌套了一个单选择决策结构。相应的流程图包括你迄今为止所学的所有内容。

练习 27.2-2 设计流程图
设计与以下代码片段相对应的流程图。
i = 30
while True:
if i % 8 == 0:
print(i, "是 8 的倍数")
if i % 4 == 0:
print(i, "是 4 的倍数")
if i % 2 == 0:
print(i, "是 2 的倍数")
i -= 2
if i <= 0: break
解决方案
这段代码片段包含一个后测试循环结构,嵌套了三个单选择决策结构。相应的流程图片段如下。

练习 27.2-3 设计流程图
设计与以下 Python 程序相对应的流程图。
for 小时 in range(1, 25):
print("小时是", 小时, ":00。")
if 小时 >= 4 且 小时 < 12:
print("早上好")
elif 小时 >= 12 且 小时 < 20:
print("下午好")
elif 小时 >= 20 且 小时 < 24:
print("晚上好")
else:
print("晚上好")
解决方案
这个 Python 程序包含一个 for 循环,嵌套了多重选择决策结构。相应的流程图如下。

练习 27.2-4 设计流程图
设计与以下 Python 程序相对应的流程图。
a = int(input())
if a == 1:
for i in range(1, 11, 2):
print(i)
elif a == 2:
for i in range(9, -1, -2):
print(i)
else:
print("没有事情要做!")
print("结束!")
解答
此 Python 程序包含一个嵌套两个 for 循环的多分支决策结构。相应的流程图如下。

练习 27.2-5 设计流程图
设计与以下 Python 程序相对应的流程图。
n = int(input())
m = int(input())
total = 0
for i in range(n):
for j in range(m):
total += i * j + j
print(total)
解答
此 Python 程序包含嵌套的循环控制结构;一个 for 循环嵌套在另一个 for 循环中。相应的流程图如下。

27.3 将流程图转换为 Python 程序
这种转换并不总是容易的。有些情况下,流程图设计者没有遵循特定的规则,因此初始流程图可能需要一些修改,才能将其转换为 Python 程序。以下是一个这样的例子。

如您所见,此流程图片段中包含的循环控制结构与您已经学习过的任何结构都不匹配,例如预测试、后测试、中测试,甚至是 for 循环控制结构。因此,您只有一个选择,那就是通过添加额外的语句或删除现有的语句来修改流程图,直到出现已知的循环控制结构。以下是一些练习,其中一些练习的初始流程图确实需要修改。
练习 27.3-1 编写 Python 程序
编写与以下流程图片段相对应的 Python 程序。
.
解答
这是一个简单的例子。你必须克服的唯一障碍是,真和假的路径并不完全在正确的位置。你需要真路径而不是假路径来实际迭代。正如你所知道的那样,你可以切换这两个路径,但你还需要否定布尔表达式。因此,相应的代码片段变为
i = 0
while i <= 90:
print(i)
i = i + 2
print("The End")
使用 for 循环,此代码片段可以等价地写成
for i in range(0, 92, 2):
print(i)
print("The End")
练习 27.3-2 编写 Python 程序
编写与以下流程图相对应的 Python 程序。

解答
此流程图包含一个后测试循环结构,嵌套一个双分支决策结构。相应的 Python 程序如下。
i = 1
while True:
if i < 45: [更多…]
print(i)
else:
print(-i)
i += 1
if i >= 90: break
print("The End")
练习 27.3-3 编写 Python 程序
编写与以下流程图相对应的 Python 程序。

解答
哎呀!真乱!这里这么多菱形!不过,要小心,因为并非所有这些都是决策控制结构。实际上,有两个是循环控制结构,只有一个代表决策控制结构!你能找到后者吗?
到目前为止,你应该已经非常熟悉循环控制结构了。正如你所知,在循环控制结构中,菱形(菱形)的一个出口总是向上方向。因此,从初始流程图中提取的以下流程图片段显然是你正在寻找的决策控制结构。
.
当然,这是一个双分支决策结构!
现在,让我们识别其余的结构。在双分支决策结构之前,有一个后测试循环结构。其流程图片段如下。
.
最后,前面提到的双分支决策结构和后测试循环结构都嵌套在下一个流程图片段中,
.
这恰好是一个预测试循环结构。相应的 Python 程序如下。
s = 0
i = 0
while i <= 99:
while True: [更多…]
n = float(input())
if n >= 0: break
if n < 100: [更多…]
s = s + n ** 2
else:
s = s + n ** 3
i += 1
print(s)
或者,你甚至可以使用 for 循环来编写它,如下面的代码所示。
s = 0
for i in range(100):
while True:
n = float(input())
if n >= 0: break
if n < 100:
s = s + n ** 2
else:
s = s + n ** 3
print(s)
毕竟,并没有那么困难,对吧?
练习 27.3-4 编写 Python 程序
编写与以下流程图相对应的 Python 程序。

解决方案
这是一个中间测试循环结构。由于没有直接对应的 Python 语句,你可以使用 break 语句——或者你可以将流程图转换为更熟悉的形式,如以下两种方法所示。
第一种方法 – 使用 break 语句
主要思想是创建一个无限循环(while True),当两个语句或语句块之间的布尔表达式评估为真时退出循环(参见第 24.3 节)。
根据这种方法,初始流程图可以用 Python 编写如下。
i = 1
S = 0
while True:
a = float(input()) [更多…]
i += 1
if i >= 90: break
S = S + a * i [更多…]
print(S)
请记住,尽管 break 语句有时可能很有用,但它也可能导致你编写难以阅读和理解的代码,尤其是在你大量使用它的时候。因此,请谨慎并适度地使用它!
第二种方法 – 转换流程图
中间测试循环结构和其等价的预测试循环结构如下。

因此,初始流程图变为

现在,编写相应的 Python 程序很容易。
i = 1
S = 0
a = float(input()) [更多…]
i += 1
while i < 90:
S = S + a * i [更多…]
a = float(input()) [更多…]
i += 1
print(S)
27.4 复习练习
完成以下练习。
1)设计与以下 Python 程序对应的流程图。
i = 35
while i > -35:
if i % 2 == 0:
print(2 * i)
else:
print(3 * i)
i -= 1
2)设计与以下 Python 程序对应的流程图。
i = -20
while True:
x = int(input())
if x == 0:
print("Zero")
elif x % 2 == 0:
print(2 * i)
else:
print(3 * i)
i += 1
if i > 20: break
3)设计与以下 Python 程序对应的流程图。
a = int(input())
if a > 0:
i = 0
while i <= a:
print(i)
i += 5
else:
print("Non-Positive Entered!")
4)设计与以下 Python 程序对应的流程图。
a = int(input())
if a > 0:
i = 0
while i <= a:
print(3 * i + i / 2)
i += 1
else:
i = 10
while True:
print(2 * i - i / 3)
i -= 3
if i < a: break
5)设计与以下 Python 程序对应的流程图。
a = int(input())
if a > 0:
for i in range(a + 1):
print(3 * i + i / 2)
elif a == 0:
b = int(input())
while b > 0:
b = int(input())
print(2 * a + b)
else:
b = int(input())
while b < 0:
b = int(input())
for i in range(a, b + 1):
print(i)
6)设计与以下 Python 程序对应的流程图。
a = int(input())
b = int(input())
c = int(input())
d = int(input())
total = 0
for i in range(a, b):
for j in range(c, d + 1, 2):
total += i + j
print(total)
7)设计与以下代码片段对应的流程图。
s = 0
for i in range(100):
n = float(input())
while n < 0:
print("Error")
n = float(input())
s += sqrt(n)
print(s)
8)设计与以下 Python 程序对应的流程图。
from math import sqrt
s = 0
for i in range(1, 51):
while True:
n = int(input())
if n >= 0: break
s += sqrt(n)
print(s)
9)设计与以下 Python 程序对应的流程图。
while True:
while True:
a = int(input())
if a >= 0: break
while True:
b = int(input())
if b >= 0: break
print(abs(a - b))
if abs(a − b) <= 100: break
10)设计与以下 Python 程序对应的流程图。
while True:
while True:
a = int(input())
b = int(input())
if a >= 0 and b >= 0: break
if a > b:
print(a - b)
else:
print(a * b)
if abs(a − b) <= 100: break
11)编写与以下流程图对应的 Python 程序。
.
12)编写与以下流程图对应的 Python 程序。

13)编写与以下流程图对应的 Python 程序。

第二十八章
循环控制结构的技巧和窍门
28.1 简介
本章致力于向您介绍一些有用的技巧和窍门,这些技巧和窍门可以帮助您编写“更好”的代码。在设计自己的算法或甚至自己的 Python 程序时,您应始终牢记这些技巧。
这些技巧和窍门可以帮助您提高代码的可读性,帮助您选择在给定问题中哪种循环控制结构更好,并帮助使代码更短或甚至更快。当然,没有单一完美的方法,因为有时使用特定的技巧或窍门可能会有所帮助,但在另一种情况下,相同的技巧或窍门可能会有完全相反的结果。大多数时候,代码优化是编程经验的问题。
较小的算法并不总是解决特定问题的最佳方案。为了解决特定问题,您可能编写了一个简洁的算法,不幸的是,这个算法证明消耗了大量的 CPU 时间以及/或大量主内存(RAM)。另一方面,您可能使用另一个算法来解决相同的问题,该算法看起来更长,但计算结果更快以及/或使用更少的 RAM。
28.2 选择循环控制结构
以下图表可以帮助您根据迭代次数选择在给定问题中使用的最合适的循环控制结构。

此图推荐最佳选项,而非唯一选项。例如,当迭代次数已知时,使用预测试或后测试循环结构并非错误。然而,提出的 for 循环更为方便。
28.3 “终极”规则
当使用预测试或后测试循环结构时,程序员常常会问的一个问题是,如何确定哪些语句应该写入循环控制结构内部,哪些应该写入外部,以及它们的顺序。
有一个简单而强大的规则——“终极”规则!一旦您遵循它,逻辑错误的潜在风险就会降至零!
“终极”规则指出:
► 参与循环布尔表达式的变量或变量必须在进入循环之前初始化。
► 变量或参与循环布尔表达式的变量值必须在循环内更新(更改)。更具体地说,执行此更新/更改的语句必须是循环的最后一个语句之一。
例如,如果变量 x 是参与循环布尔表达式的变量,则预测试循环结构应采用以下形式,
初始化 x
while Boolean_Expression(x):
一个语句或语句块
更新/更改 x
以及后测试循环结构应采用以下形式,
初始化 x
while True:
一个语句或语句块
更新/更改 x
if Boolean_Expression(x): break
其中
►初始化 x 是任何为变量 x 分配初始值的语句。它可以是输入语句,如 input("输入一个数字:"),或者使用值赋值运算符(=)的赋值语句。然而,在后测试循环结构中,这个语句有时可能是多余的,可以省略,因为 x 的初始化可以直接在循环内部进行。
►布尔 _ 表达式(x)可以是任何从简单到复杂的布尔表达式,取决于变量 x。
►更新/修改 x 是任何改变 x 值的语句,例如另一个输入语句,使用值赋值运算符(=)的赋值语句,或者甚至是复合赋值运算符。重要的是,这个语句必须位于循环布尔表达式评估点之前。这意味着它应该是循环中最后的语句之一。
以下是一些使用“终极”规则的示例。
示例 1
a = int(input()) #a 的初始化
while a > 0: #依赖于 a 的布尔表达式
print(a)
a = a – 1 #a 的更新/修改
示例 2
a = int(input()) #a 的初始化
b = int(input()) #b 的初始化
while a > b: #依赖于 a 和 b 的布尔表达式
print(a, b)
a = int(input()) #a 的更新/修改
b = int(input()) #b 的更新/修改
示例 3
s = 0 #s 的初始化
while True:
y = int(input())
s = s + y #s 的更新/修改
if s >= 1000: break #依赖于 s 的布尔表达式
示例 4
y = 0 #y 的初始化
while True:
y = int(input()) #y 的更新/修改
if y >= 0: break #依赖于 y 的布尔表达式
在这个例子中,尽管变量 y 在循环外部的初始化是多余的,可以省略,如下所示。
while True:
y = int(input()) #y 的初始化和更新/修改
if y >= 0: break #依赖于 y 的布尔表达式
示例 5
odd = 0 #odd 的初始化
even = 0 #even 的初始化
while odd + even < 5: #依赖于 odd 和 even 的布尔表达式
x = int(input())
if x % 2 == 0:
even += 1 #even 的更新/修改
else:
odd += 1 #odd 的更新/修改
print("奇数:", odd, "偶数:", even)
现在,你会意识到为什么你应该始终遵循“终极”规则!让我们看看以下练习:
编写一个代码片段,允许用户重复输入数字,直到总共输入了三个正数。
这个练习被给了一个班级,一个学生给出了以下代码片段作为答案。
positivesCount = 0
x = float(input())
while positivesCount != 3:
if x > 0:
positivesCount += 1
x = float(input())
print("提供了三个正数!")
初看之下,这似乎是正确的。它允许用户输入一个数字,进入循环,检查用户提供的数字是否为正数,然后让用户输入第二个数字,以此类推。然而,这段代码中存在一个逻辑错误——而且不幸的是,这是一个棘手的错误。你能找到它吗?
通过尝试各种输入值(正数、负数或零)来遵循执行流程。当用户输入一个正数时,变量正数计数增加一个;而当他们输入一个负数或零时,它保持不变。一切看起来似乎运行得都很顺利,不是吗?——如此顺利,以至于你可能质疑这本书是否可靠,或者你是否应该扔掉它!
只有当用户尝试输入所有三个预期的正数值时,问题才会变得明显。下面的跟踪表可以帮助你确定问题所在。假设用户想要输入的值是 5、-10、-2、4 和 20。
| 步骤 | 说明 | 备注 | 正数计数 | x |
|---|---|---|---|---|
| 1 | 正数计数 = 0 | 0 | ? | |
| 2 | x = float(input()) | 0 | 5.0 | |
| 3 | while 正数计数 != 3 | 这将评估为 True | ||
| 4 | if x > 0 | 这将评估为 True | ||
| 5 | 正数计数 += 1 | 1 | 5.0 | |
| 6 | x = float(input()) | 1 | -10.0 | |
| 7 | while 正数计数 != 3 | 这将评估为 True | ||
| 8 | if x > 0 | 这将评估为 False | ||
| 9 | x = float(input()) | 1 | -2.0 | |
| 10 | while 正数计数 != 3 | 这将评估为 True | ||
| 11 | if x > 0 | 这将评估为 False | ||
| 12 | x = float(input()) | 1 | 4.0 | |
| 13 | while 正数计数 != 3 | 这将评估为 True | ||
| 14 | if x > 0 | 这将评估为 True | ||
| 15 | 正数计数 += 1 | 2 | 4.0 | |
| 16 | x = float(input()) | 2 | 20.0 | |
| 17 | while 正数计数 != 3 | 这将评估为 True | ||
| 18 | if x > 0 | 这将评估为 True | ||
| 19 | 正数计数 += 1 | 3 | 20.0 | |
| 20 | x = float(input()) | 3 | ??? |
这里就是逻辑错误!在第 20 步,尽管用户提供的正数总数是三个,你期望执行结束,但不幸的是,用户被要求再输入一个数字!但是,你需要一个代码片段让用户输入三个正数,而不是四个,对吧?
这就是为什么你应该始终按照书上的来做!让我们看看这个代码片段应该如何编写。
由于 while 循环的布尔表达式依赖于变量正数计数,因此这个变量必须在循环外部初始化。此变量还必须在循环内部更新/更改。执行此更新/更改的语句必须是循环中的最后一个语句,如下面的代码片段(以一般形式)所示。
正数计数 = 0 # 正数计数的初始化
while 正数计数 != 3: # 这依赖于正数计数
一个语句或语句块
if x > 0:
正数计数 += 1 # 正数计数的更新/更改
现在你可以添加任何必要的语句来完成代码。你只需要添加的语句是允许用户输入一个数字的语句(这必须在循环内部完成),以及显示最后信息的语句(这必须在循环完成所有迭代时完成)。因此,最终的代码片段变为
positivesCount = 0
当 positivesCount 不等于 3 时:
x = float(input())
如果 x 大于 0:
positivesCount += 1
打印("提供了三个正数!")
28.4 从循环中退出
循环可能会消耗过多的 CPU 时间,所以当你使用它们时必须非常小心。有时在满足特定条件之前,你需要跳出或结束循环,通常是在满足特定条件时。
假设有一个隐藏的密码,你某种方式知道它由三个字符组成,只包含数字。下面的 for 循环通过暴力攻击尝试在 900 次迭代中找到那个隐藏的密码。
found = False
for i in range(100, 1000):
如果 i 等于 hiddenPassword:
password = i
found = True
如果 found 等于 True:
打印("隐藏的密码是:", password)
暴力攻击是获取任何受密码保护的访问权限的最简单方法。攻击者尝试字母、数字和符号的组合,希望最终猜对。
现在,假设隐藏的密码是 123。正如你所知,for 循环会迭代指定的次数,在这种情况下,它并不关心是否找到了隐藏的密码。即使密码在 24 次迭代中被找到,循环仍然不幸地继续迭代,直到变量 i 达到 999 的值,从而浪费了 CPU 时间。
有人可能会说 800 – 900 次迭代并不是什么大问题,他们可能是对的。然而,在大规模数据处理中,每一次迭代都很重要。因此,在使用循环控制结构时,你应该非常小心,特别是那些迭代次数太多的结构。如果隐藏的密码是十位数长呢?这意味着 for 循环将不得不执行 9,000,000,000 次迭代!
有两种方法可以帮助你使像前面的程序一样运行得更快。这两种方法的主要思想都是在满足特定条件时跳出循环;在这种情况下,当找到隐藏的密码时。
第一种方法 - 使用 break 语句
你可以使用 break 语句在循环实际完成所有迭代之前跳出循环。
看看下面的 Python 程序。当隐藏的密码被找到时,执行流程立即退出(跳出)for 循环。
found = False
for i in range(100, 1000):
如果 i 等于 hiddenPassword:
password = i
found = True
break
如果 found:
打印("隐藏的密码是:", password)
if found 等价于 if found == True
第二种方法 - 使用标志
break 语句并不是所有计算机语言都有的;由于这本书的目的是教你“算法思维”(而不仅仅是 Python 支持的特殊语句),让我们看看一种替代方法。
在下面的 Python 程序中,当隐藏的密码被找到时,布尔表达式 found == False 强制执行流程退出循环。
found = False
i = 100
while found == False and i <= 999:
if i == hiddenPassword:
password = i
found = True
i += 1
if found:
print("隐藏的密码是:", password)
将找到的变量视为标志。最初,标志没有被“升起”(found = False)。执行流程进入循环,只要标志保持未升起状态(while found == False …),就会持续迭代。当循环内部发生使标志升起(将 True 赋值给变量 found)的事件时,执行流程将退出循环。
while found == False and i <= 999 可以改写为 while not found and i <= 999。
i <= 999 的布尔表达式仍然是必要的,以防隐藏的密码没有找到。
28.5 清理循环
如前所述,循环可能会消耗过多的 CPU 时间,所以你必须非常小心,并且要谨慎使用它们。尽管有时大量的迭代是不可避免的,但总有你可以做的事情来提高循环的性能。
下面的代码片段计算了数字 1, 2, 3, 4, 5, … 10000 的总和。
s = 0
i = 1
while True:
countOfNumbers = 10000
s = s + i
i += 1
if i > countOfNumbers : break
print(s)
在使用循环时,尤其是执行多次迭代的循环时,你应该始终牢记一点:避免在循环中放置任何在该循环中没有任何目的的语句。在上一个例子中,countOfNumbers = 10000 这条语句就是这样一条语句。不幸的是,只要它存在于循环中,计算机就会无理由地执行它 10000 次,这当然会影响计算机的性能。
为了解决这个问题,你可以简单地把这个语句移出循环,如下所示。
countOfNumbers = 10000
s = 0
i = 1
while True:
s = s + i
i += 1
if i > countOfNumbers: break
print(s)
练习 28.5-1 清理循环
下面的代码片段计算了数字 1, 2, 3, 4, … 10000 的平均值。尽量将尽可能多的语句移出循环,以提高程序效率。
s = 0
for i in range(1, 10001):
s = s + i
average = s / 10000
print(average)
解决方案
初学者在计算平均值时犯的一个非常常见的错误是将将总和除以总和中的数字数量的语句(这里 average = s / 10000)放在循环中。想想看!想象一下你想计算你在学校的学习平均成绩。你的第一步是计算你正在上的 10 门课程的分数总和。然后,当所有分数都加起来后,你会将这个总和除以 10。这意味着你会执行 10 次加法运算和 1 次除法运算。
计算平均值是一个两步过程。
因此,在循环中计算平均值是没有意义的。你可以将这个语句移到循环外,紧接在循环之后,并让循环仅用于求和,如下所示。
s = 0
for i in range(1, 10001):
s = s + i
average = s / 10000
打印(average)
练习 28.5-2 清理循环
下一个公式
.
使用以下 Python 程序解决,其中 N 由用户提供。
n = int(input("输入 N: "))
s = 0
for i in range(1, n + 1):
denom = 0
for j in range(1, n + 1):
denom += j ** j
s += i / denom
打印(s)
尽量将尽可能多的语句移出循环,以提高程序效率。
解答
如公式所示,分母对所有分数都是共同的。因此,对每个分数重复计算它是没有意义的。您可以只计算一次分母,然后多次使用结果,如下所示。
n = int(input("输入 N: "))
denom = 0 [更多…]
for j in range(1, n + 1):
denom += j ** j
s = 0
for i in range(1, n + 1):
s += i / denom
打印(s)
28.6 无限循环及其停止方法
所有 while 循环都必须包含一些最终导致执行流程退出循环的内容。但错误确实会发生!例如,以下代码片段包含一个无限循环。不幸的是,程序员忘记在循环中增加变量 i;因此,变量 i 永远无法达到 10 的值。
i = 1
while i != 10:
打印("Hello there!")
如果一个循环无法停止迭代,它被称为无限循环或死循环。
程序员可能犯的另一个错误如下:
i = 1
while i != 10:
打印("Hello there!")
i += 2
尽管这个代码片段确实包含一个在循环中增加变量 i 的语句(i += 2),但不幸的是,执行流程永远不会退出循环,因为变量 i 永远不会被赋值为 10。
无限循环会永远迭代,唯一停止其迭代的方法是使用魔法力量!例如,当一个在 Windows 操作系统中的应用程序“挂起”(可能是因为执行流程进入了一个无限循环),用户必须使用键组合 ALT+CTRL+DEL 来强制结束应用程序。
在 IDLE 中,如果你执行一个进入无限循环的代码,你可以按 CTRL+C 键组合,Python 编译器将停止执行。
在 Visual Studio Code 中,当你意外编写并执行一个无限循环时,你可以简单地点击“停止”
工具栏图标,执行将停止。
在 Visual Studio Code 中,强制应用程序结束的另一种方法是,在终端窗口中使用键组合 CTRL+C。
28.7 从内到外的方法
从内到外是本书提出的一种方法,旨在帮助你从内部学习“算法思维”。这种方法首先操纵和设计内部(嵌套)控制结构,然后,随着算法(或程序)的发展,会添加越来越多的控制结构,将先前的结构嵌套其中。这种方法可以用于大型和复杂控制结构,因为它有助于你设计无错误的流程图或甚至 Python 程序。本书在似乎必要的地方都使用了这种方法。
让我们尝试以下示例。
编写一个 Python 程序,以如下所示的方式显示以下乘法表。

根据“从内到外”方法,你首先编写内部控制结构,然后,当一切测试并运行正常后,你可以添加外部控制结构(s)。
因此,让我们尝试只显示乘法表的第一行。如果你检查这一行,它会揭示在每次乘法中,乘数总是 1。让我们将乘数视为变量 i,其值为 1。显示乘法表第一行的循环控制结构如下。
代码片段 1
for j in range(1, 11):
print(i, "x", j, "=", i * j, end = "\t")
如果你执行此代码片段,结果是

特殊字符序列\t“显示”在每个迭代后一个制表符。这确保了所有内容都正确对齐。
内部(嵌套)循环控制结构已就绪。你现在需要一种方法来执行这个控制结构九次,但每次变量 i 必须包含不同的值,从 1 到 9。这可以通过以下方式实现。
主代码
for i in range(1, 10):
代码片段 1:显示乘法表的一行
print()
print()语句用于在行之间“显示”换行符。
将代码片段 1 嵌入主代码后,最终的 Python 程序变为
file_28.7
for i in range(1, 10):
for j in range(1, 11): [更多…]
print(i, "x", j, "=", i * j, end = "\t")
print()
28.8 复习问题:正确/错误
对以下每个陈述选择正确或错误。
1)当迭代次数未知时,你可以使用确定循环。
2)当迭代次数已知时,你不能使用后测试循环结构。
3)根据“终极”规则,在预测试循环结构中,参与循环布尔表达式的变量的初始化必须在循环内部完成。
4)根据“终极”规则,在预测试循环结构中,更新/更改参与循环布尔表达式的变量值的语句必须是循环内的最后一个语句。
5)根据“终极”规则,在测试后的循环结构中,参与循环布尔表达式的变量的初始化有时可以在循环内部完成。
6)根据“终极”规则,在测试前的循环结构中,参与循环布尔表达式的变量的更新/修改必须是循环内的第一条语句。
7)在 Python 中,你可以使用 exit 语句在循环完成所有迭代之前跳出循环。
8)将常量值赋给变量的语句最好放在循环控制结构内部。
9)在以下代码片段中,至少有一个语句可以被移出 for 循环。
for i in range(30):
a = "Hello"
print(a)
10)在以下代码片段中,至少有一个语句可以被移出 while 循环。
s = 0
count = 1
while count < 100:
a = int(input())
s += a
average = s / count
count += 1
print(average)
11)在以下代码片段中,至少有一个语句可以被移出 while 循环。
s = 0
y = int(input())
while y != -99:
s = s + y
y = int(input())
12)以下代码片段满足有限性的属性。
i = 1
while i != 100:
print("Hello there!")
i += 5
13)当在测试前的循环结构的布尔表达式中使用不等于(!=)比较运算符时,循环总是无限迭代。
14)以下代码片段满足有限性的属性。
i = 0
while True:
print("Hello there!")
i += 5
if i >= 100: break
28.9 复习问题:多项选择题
选择以下每个陈述的正确答案。
1)当迭代次数未知时,你可以使用
a)测试前的循环结构。
b)测试后的循环结构。
c)所有上述选项
2)当迭代次数已知时,你可以使用
a)测试前的循环结构。
b)测试后的循环结构。
c)a for 循环。
d)所有上述选项。
3)根据“终极”规则,在测试前的循环结构中,参与循环布尔表达式的变量的初始化必须
a)循环内部。
b)循环外部。
c)所有上述选项。
4)根据“终极”规则,在测试前的循环结构中,参与循环布尔表达式的变量的更新/修改必须
a)循环内部。
b)循环外部。
c)所有上述选项。
5)根据“终极”规则,在测试后的循环结构中,参与循环布尔表达式的变量的初始化可以
a)循环内部。
b)循环外部。
c)所有上述选项。
6)在以下代码片段中
s = 0
for i in range(100):
s = s + i
x = 100.0
average = s / x
可以移出 for 循环的语句数量是
a)0。
b)1。
c)2。
d)3。
7)当这个比较运算符在测试后的循环结构的布尔表达式中使用时,循环将永远迭代。
a)==
b)!=
c)视情况而定
28.10 复习练习
完成以下练习。
1)以下程序旨在提示用户重复输入名字,直到输入“STOP”(用作名字)为止。最后,程序必须显示输入的总名字数以及其中有多少个不是“John”。
countNames = 0
countNotJohns = 0
name = ""
while name != "STOP":
name = input("输入一个名字:")
countNames += 1
if name != "John":
countNotJohns += 1
print("输入的总名字数:", countNames)
打印("输入的除 John 以外的名字有:", countNotJohns)
然而,程序显示的结果是错误的!使用“终极”规则,尝试修改程序以显示正确的结果。
2)编写一个 Python 程序,提示用户输入一些文本。文本可以是单个单词或整个句子。然后,程序必须显示一条消息,说明用户提供的文本是单个单词还是完整的句子。
提示:搜索空格字符!如果找到空格字符,则意味着用户输入了一个句子。程序必须在找到至少一个空格字符时停止搜索。
3)编写一个 Python 程序,提示用户输入一个句子。然后,程序必须显示消息“句子中包含数字”如果句子中至少包含一个数字。程序必须在找到至少一个数字时停止搜索。
4)修正以下代码片段,使其不会无限循环。
print("打印 1 到 100 的所有整数")
i = 1
while i < 101:
print(i)
5)修正以下循环控制结构的布尔表达式,使其不会无限循环。
print("打印 1 到 99 的奇数")
i = 1
while not(i == 100):
print(i)
i += 2
6)以下代码片段计算用户输入的 100 个数的平均值。尽量将尽可能多的语句移出循环以提高效率。
s = 0
i = 1
while True:
count = 100
number = float(input())
s = s + number
average = s / count
i += 1
if i > count: break
print(average)
7)以下公式
.
is solved using the following Python program.
s = 0
for i in range(1, 101):
denom = 1
for j in range(1, 101):
denom *= j
s += i / denom
print(s)
尽量将尽可能多的语句移出循环以提高效率。
8)编写一个 Python 程序,显示 1 到 4 之间整数对的所有组合以及它们的乘积。输出必须如下所示。
1 x 1 = 1
1 x 2 = 2
1 x 3 = 3
1 x 4 = 4
2 x 1 = 2
2 x 2 = 4
2 x 3 = 6
2 x 4 = 8
…
…
4 x 1 = 4
4 x 2 = 8
4 x 3 = 12
4 x 4 = 16
9)编写一个 Python 程序,显示 1 到 12 之间整数对的乘法表,如下所示。请注意,输出使用制表符对齐。

- 编写一个 Python 程序,提示用户输入一个整数,然后显示从 1 到该整数之间的整数对乘法表。例如,如果用户输入的值是 5,输出必须像下面所示。请注意,输出是按制表符对齐的。

第二十九章
更多关于循环控制结构的练习
29.1 使用循环控制结构的简单练习
练习 29.1-1 根据哪个更大来计数
编写一个 Python 程序,提示用户输入 10 对数字,然后计算并显示第一个用户提供的数字大于第二个数字的次数和第二个数字大于第一个数字的次数。
解决方案
Python 程序如下。它使用变量 countA 来计数第一个用户提供的数字大于第二个数字的次数,并使用变量 countB 来计数第二个数字大于第一个数字的次数。
file_29.1-1
countA = 0
countB = 0
for i in range(10):
a = int(input("输入数字 A: "))
b = int(input("输入数字 B: "))
if a > b:
countA += 1
elif b > a:
countB += 1
print(countA, countB)
可能有人会问一个合理的问题:“为什么使用多决策控制结构?为什么不使用双分支决策结构?”
假设,确实,使用了以下的双分支决策结构。
if a > b:
countA += 1
else:
countB += 1
在这个决策控制结构中,当变量 b 大于变量 a 时(这是期望的),变量 countB 会增加,但当变量 b 等于变量 a 时(这是不期望的),变量 countB 也会增加。使用多决策控制结构可以确保只有当变量 b 大于(而不是等于)变量 a 时,变量 countB 才会增加。
练习 29.1-2 根据它们的位数来计数
编写一个 Python 程序,提示用户输入 20 个整数,然后计算并显示一位数、两位数和三位数的总数。假设用户输入的值在 1 到 999 之间。
解决方案
使用练习 18.1-3 中的知识,Python 程序如下所示。
file_29.1-2
count1 = count2 = count3 = 0
for i in range(20):
a = int(input("输入一个数字: "))
if a <= 9:
count1 += 1
elif a <= 99:
count2 += 1
else:
count3 += 1
print(count1, count2, count3)
练习 29.1-3 在一个和中可以放入多少个数
编写一个 Python 程序,让用户重复输入数值,直到它们的总和超过 1000。最后,程序必须显示输入的总数。
解决方案
在这种情况下,由于迭代的确切次数未知,不能使用确定循环;需要使用不确定循环。让我们使用预测试循环来创建这个循环。然而,为了确保程序没有逻辑错误,必须遵守在第 28.3 节中讨论的“终极”规则。根据此规则,预测试循环的结构应如下所示,以一般形式给出。
初始化总数
while total <= 1000 :
一个语句或语句块
更新/修改总数
由于循环的布尔表达式依赖于变量 total,因此必须在循环开始之前初始化该变量,并在循环内部更新(修改)。更具体地说,更新/修改变量 total 的语句必须是循环的最后一个语句。遵循此规则后,Python 程序变为
file_29.1-3
count = 0
total = 0 # total 的初始化
while total <= 1000: # 依赖于 total 的布尔表达式
x = float(input())
count += 1
total += x # 总数的更新/修改
print(count)
练习 29.1-4 求正整数的总数
编写一个 Python 程序,提示用户重复输入整数,直到输入一个实数为止。最后,程序必须显示输入的正整数总数。
解答
再次强调,你不知道确切的迭代次数,因此不能使用 for 循环。
根据“终极”规则,预测试循环结构应如下所示,以一般形式给出。
x = float(input("输入一个数字: ")) # x 的初始化
while int(x) == x: # 依赖于 x 的布尔表达式
一个语句或语句块
x = float(input("输入一个数字: ")) # x 的更新/修改
最终的 Python 程序如下。
file_29.1-4
count = 0
x = float(input("输入一个数字: "))
while int(x) == x:
if x > 0:
count += 1
x = float(input("输入一个数字: "))
print(count)
注意,即使第一个用户提供的数字是实数(浮点数),程序也能正常工作;预测试循环结构确保执行流程永远不会进入循环以处理任何实数!
练习 29.1-5 用户希望迭代多少次
编写一个 Python 程序,提示用户输入两个数字,然后计算并显示第一个数字的第二个数字次幂。程序必须根据用户的意愿迭代多次。每次计算结束后,程序必须提示用户是否希望再次计算。如果答案是“yes”,程序必须重复;否则必须结束。程序应接受所有可能的答案形式,如“yes”、“YES”、“Yes”、“YeS”。
解答
根据“终极”规则,预测试循环结构应如下所示,以一般形式给出。
answer = "yes" # answer 的初始化
while answer.upper() != "YES":
提示用户输入两个数字,然后计算并显示第一个数字的第二个数字次幂。
更新/修改 answer
answer = input("你想重复吗? ")
upper() 方法确保程序对任何用户提供的答案(“yes”、“YES”、“Yes”、“YeS”或“yEs”)都能正确运行!
然而,这次我们不是使用预测试循环结构,而是使用后测试循环结构。这是一个更好的方法,因为可以在循环外部省略答案变量的初始化。与预测试循环结构不同,执行流程无论哪种方式都会进入循环,答案的初始化将在后测试循环内部完成,如下面的代码片段(以一般形式给出)所示。
while True:
提示用户输入两个数字,然后计算并显示第一个数字的第二个数字次幂。
初始化和答案的更新/修改
answer = input("你想重复吗?")
if answer.upper() != "YES": break
这个练习的解决方案变为
file_29.1-5
while True:
print("请输入两个数字:")
a = int(input())
b = int(input())
result = a ** b
print("结果是:", result)
answer = input("你想重复吗?")
if answer.upper() != "YES": break
练习 29.1-6 求各位数字之和
编写一个 Python 程序,允许用户输入一个整数,然后计算其各位数字之和。
解答
在练习 13.1-2 中,你学习了如何在知道整数总位数的情况下分割整数的数字。然而,在这个练习中,用户可以输入任何值,无论大小。因此,数字的总数是一个未知量。
为了解决这个练习,可以使用循环控制结构。然而,您可以使用两种方法。
第一种方法
在这种方法中,主要思想是在每次迭代中隔离一个数字。然而,挑战在于确定所需的迭代总数,因为它取决于用户提供的整数的大小。那么,这会成为一个障碍吗?当然不会!
在循环中,用户提供的整数应该在每个迭代中连续减少,直到最终达到零。这个零值可以作为停止循环控制结构迭代的条件。例如,如果用户提供的数字是 4753,它应该在第一次迭代中变为 475,在第二次迭代中变为 47,然后变为 4,最终变为 0。一旦它达到 0,迭代必须停止。
让我们尝试使用以下流程图来理解所提出的解决方案。其中一些语句是以一般形式编写的。

语句
digit ← 隔离变量 x 的最后一位数字。
可以使用众所周知的 MOD 10 运算符,如下所示。
digit ← x MOD 10
然而,整个概念依赖于以下语句
x ← 保留变量 x 的所有数字,除了最后一位。
这是最终将变量 x 的值置零的语句,然后执行流程退出循环。要编写此语句,您可以使用如下所示的 DIV 10 运算符。
x ← x DIV 10
因此,Python 程序变为
file_29.1-6a
x = int(input())
s = 0
while x != 0:
digit = x % 10 #这是 x MOD 10 操作
s = s + digit
x = x // 10 #这是 x DIV 10 操作
print(s)
让我们为输入值 4753 创建一个跟踪表,以更好地理解实际发生的情况。
| 步骤 | 语句 | 备注 | x | digit | s |
|---|---|---|---|---|---|
| 1 | x = int(input()) | 用户输入的值为 4753 | 4753 | ? | ? |
| 2 | s = 0 | 4753 | ? | 0 | |
| 3 | while x != 0: | 这将评估为 True | |||
| 4 | digit = x % 10 | 4753 | 3 | 0 | |
| 5 | s = s + digit | 4753 | 3 | 3 | |
| 6 | x = x // 10 | 475 | 3 | 3 | |
| 7 | while x != 0: | 这将评估为 True | |||
| 8 | digit = x % 10 | 475 | 5 | 3 | |
| 9 | s = s + digit | 475 | 5 | 8 | |
| 10 | x = x // 10 | 47 | 5 | 8 | |
| 11 | while x != 0: | 这将评估为 True | |||
| 12 | digit = x % 10 | 47 | 7 | 8 | |
| 13 | s = s + digit | 47 | 7 | 15 | |
| 14 | x = x // 10 | 4 | 7 | 15 | |
| 15 | while x != 0: | 这将评估为 True | |||
| 16 | digit = x % 10 | 4 | 4 | 15 | |
| 17 | s = s + digit | 4 | 4 | 19 | |
| 18 | x = x // 10 | 0 | 4 | 19 | |
| 19 | while x != 0: | 这将评估为 False | |||
| 20 | print(s) | 显示:19 |
第二种方法
在这种方法中,主要思想是将用户提供的整数转换为字符串,然后使用 for 循环遍历其所有字符(数字)。然而,在 for 循环中,在将每个数字累积到变量 s 之前,需要将每个数字从字符串类型转换回整数类型。Python 程序如下。
file_29.1-6b
x = int(input())
s = 0
for digit in str(x):
s += int(digit)
print(s)
29.2 使用嵌套循环控制结构的练习
练习 29.2-1 显示包含给定数字的所有三位数
编写一个 Python 程序,提示用户输入一个数字(0 到 9),然后显示所有包含该用户提供的数字至少一次的三位数。例如,对于用户提供的值 7,357、771 和 700 都是这样的整数。
解决方案
有三种不同的方法!第一种只使用一个 for 循环,第二种使用三个 for 循环,嵌套一个在另一个中,最后一种使用 Python 的魔法力量。让我们分析它们所有!
首种方法 – 使用 for 循环和决策控制结构
主要思想是使用一个 for 循环,其中变量 counter 的值从 100 到 999。在循环中,将 counter 变量分解为其各个数字(digit[3]、digit[2]、digit[1]),并使用决策控制结构检查其数字中至少有一个等于提供的数字。Python 程序如下。
file_29.2-1a
x = int(input("输入一个数字 0 - 9: "))
for i in range(100, 1000):
digit3 = i // 100
r = i % 100
digit2 = r // 10
digit1 = r % 10
if digit3 == x or digit2 == x or digit1 == x:
print(i)
经过一些改进后,程序可以如下改进
file_29.2-1b
x = int(input("输入一个数字 0 - 9: "))
for i in range(100, 1000):
digit3, r = divmod(i, 100)
digit2, digit1 = divmod(r, 10)
if x in [digit1, digit2, digit3]:
print(i)
第二种方法 - 使用嵌套循环控制结构和决策控制结构
这里的主要思想是使用三个嵌套的 for 循环。在这种情况下,有三个计数器变量(digit3、digit2 和 digit1),每个变量对应三位整数的一个数字。Python 程序如下。
file_29.2-1c
x = int(input("Enter a digit 0 - 9: "))
for digit3 in range(1, 10):
for digit2 in range(10):
for digit1 in range(10):
if x in [digit1, digit2, digit3]:
print(digit3 * 100 + digit2 * 10 + digit1)
如果您遵循执行流程,100 是第一个“整数”评估的值(digit3 = 1,digit2 = 0,digit1 = 0)。然后,最内层的循环控制结构将变量 digit1 加一,下一个评估的值是“整数”101。这个过程一直持续到 digit1 达到 9 的值;即,“整数”达到 109 的值。执行流程然后退出最内层的循环控制结构,变量 digit2 加一,最内层的循环控制结构重新开始,因此评估的值是“整数”110、111、112、… 119。这个过程一直持续到评估所有值直到 999 的整数。
注意变量 digit3 从 1 开始,而变量 digit2 和 digit1 从 0 开始。这是必要的,因为三位数的范围从 100 开始,而不是从 000 开始。
注意打印语句如何组合三位整数。
第三种方法 - Python 风格(版本 1.0)
在这种方法中,for 循环的计数器变量被转换为字符串,并像下面这样解包成三个单独的变量。
file_29.2-1d
x = input("Enter a digit 0 - 9: ")
for i in range(100, 1000):
digit3, digit2, digit1 = str(i)
if x in [digit3, digit2, digit1]:
print(i)
注意变量 x,以及变量 digit1、digit2 和 digit3,都是字符串类型。
第四种方法 - Python 风格(版本 2.0)
在这种方法中,for 循环的计数器变量被转换为字符串,并且 in 操作符检查用户提供的“数字”是否存在于字符串中。Python 程序如下。
file_29.2-1e
x = input("Enter a digit 0 - 9: ")
for i in range(100, 1000):
if x in str(i):
print(i)
注意变量 x 是字符串类型。
练习 29.2-2 显示指定条件的所有实例
编写一个 Python 程序,显示所有三位整数,其中第一位数字小于第二位数字,第二位数字小于第三位数字。例如,357、456 和 159 是这样的整数。
解答
利用上一练习(练习 29.2-1)的知识,有三种不同的方法!让我们分析一下所有的方法!
第一种方法 – 使用 for 循环和决策控制结构
使用 for 循环和决策控制结构,Python 程序如下。
file_29.2-2a
for i in range(100, 1000):
digit3, r = divmod(i, 100)
digit2, digit1 = divmod(r, 10)
if digit3 < digit2 and digit2 < digit1:
print(i)
第二种方法 – 使用嵌套循环控制结构和决策控制结构
使用嵌套循环控制结构和决策控制结构,Python 程序如下。
file_29.2-2b
for digit3 in range(1, 10):
for digit2 in range(10):
for digit1 in range(10):
if digit3 < digit2 and digit2 < digit1:
print(digit3 * 100 + digit2 * 10 + digit1)
第三种方法 – 仅使用嵌套循环控制结构
这种方法基于第二种方法。它们之间的主要区别在于,在这种情况下,变量 digit1 始终从大于 digit2 的值开始,而变量 digit2 始终从大于 digit3 的值开始。这样,第一个将显示的整数是 123。
没有小于 123 和大于 789 的整数可以验证布尔表达式 digit3 < digit2 和 digit2 < digit1 为真。
以下是一个 Python 程序。
file_29.2-2c
for digit3 in range(1, 8):
for digit2 in range(digit3 + 1, 9):
for digit1 in range(digit2 + 1, 10):
print(digit3 * 100 + digit2 * 10 + digit1)
这种解决方案是最有效的,因为它没有使用任何决策控制结构,而且迭代次数保持在最小!
如您所见,一个问题可以有多个解决方案。取决于您找到最优解!
29.3 使用循环控制结构进行数据验证
正如您已经知道的,数据验证是限制数据输入的过程,这迫使用户只能输入有效的值。您已经遇到了一种使用决策控制结构的数据验证方法。让我们回顾一个例子。
from math import sqrt
x = float(input("Enter a non-negative number: "))
if x < 0:
print("Error: Negative number entered!")
else:
print(sqrt(x))
然而,这种方法可能对用户来说并不方便。如果他们输入了一个无效的数字,程序会显示错误信息,执行流程不可避免地达到末尾。然后,用户必须重新启动程序以重新输入一个有效的数字。
接下来,您将找到三种使用循环控制结构验证数据输入的一般形式的解决方案。在用户输入无效值的情况下,主要目标是反复提示他们,直到他们最终提供一个有效的值。当然,如果用户最初输入了一个有效的值,执行流程将简单地继续到程序的下一部分。
你选择哪种方法取决于你是否希望显示错误信息,以及你是否希望为每种输入错误显示不同的错误信息,或者只是显示一个通用的错误信息来处理任何类型的错误。
第一种方法 – 无错误信息的验证数据输入
要在没有显示任何错误信息的情况下验证数据输入,你可以使用以下代码片段,以通用形式给出。
while True:
input_data = input("提示信息")
if input_data 测试 1 成功且
input_data 测试 2 成功且
…
input_data 测试 N 成功:break
第二种方法 – 使用通用错误信息验证数据输入
要验证数据输入并显示一个通用的错误信息(即,对于任何类型的输入错误都是相同的错误信息),你可以使用以下代码片段,以通用形式给出。
input_data = input("提示信息")
while input_data 测试 1 失败或
input_data 测试 2 失败或
…
input_data 测试 N 失败:
print("错误信息")
input_data = input("提示信息")
第三种方法 – 使用不同的错误信息验证数据输入
要验证数据输入并显示每种输入错误的特定错误信息,你可以使用以下代码片段,以通用形式给出。
while True:
input_data = input("提示信息")
failure = False
if input_data 测试 1 失败:
print("错误信息 1")
failure = True
elif input_data 测试 2 失败:
print("错误信息 2")
failure = True
elif …
…
elif input_data 测试 N 失败:
print("错误信息 N")
failure = True
if not failure: break
if not failure 等价于 if failure == False
练习 29.3-1 寻找奇偶数 - 无错误信息的验证
编写一个 Python 程序,提示用户输入一个非负整数,然后显示一条消息,指出该数字是偶数;如果不是,则显示“Odd”。使用循环控制结构,程序还必须验证数据输入,允许用户只能输入一个非负整数。
解答
在 第 29.3 节 中学习到的所有三种验证数据输入的方法都将在此处展示。但首先,让我们在不进行数据验证的情况下解决这个练习。
x = float(input("输入一个非负整数: ")) [更多…]
if x % 2 == 0:
print("Even")
else:
print("Odd")
无错误信息的验证
要在没有显示任何错误信息的情况下验证数据输入,请使用 第 29.3 节 中的第一种方法。只需将带有虚线框的语句替换为以下代码片段。
while True:
x = float(input("输入一个非负整数: "))
if x >= 0 and int(x) == x: break
在数据输入阶段,使用的是 float() 函数而不是 int() 函数。这是为了允许用户输入整数或浮点数。
最终的 Python 程序变为
file_29.3-1a
while True: [更多…]
x = float(input("Enter a non-negative integer: "))
if x >= 0 and int(x) == x: break
if x % 2 == 0:
print("Even")
else:
print("Odd")
Validation with a Generic Error Message
为了验证数据输入并显示一个通用的错误信息,将带有虚线矩形的语句替换为基于第 29.3 节的第二种方法的代码片段。Python 程序如下。
file_29.3-1b
x = float(input("Enter a non-negative integer: ")) [更多…]
while x < 0 or x != int(x):
print("Error! A negative value or a float entered.")
x = float(input("Enter a non-negative integer: "))
if x % 2 == 0:
print("Even")
else:
print("Odd")
Validation with Different Error Messages
在这里,替换的代码片段基于第 29.3 节的第三种方法。为了验证数据输入并针对每种输入错误显示不同的错误信息,Python 程序如下。
file_29.3-1c
while True: [更多…]
x = float(input("Enter a non-negative integer: "))
failure = False
if x < 0:
print("Error! You entered a negative value")
failure = True
elif x != int(x):
print("Error! You entered a float")
failure = True
if not failure: break
if x % 2 == 0:
print("Even")
else:
print("Odd")
Exercise 29.3-2 Finding the Sum of Four Numbers
编写一个 Python 程序,提示用户输入四个正数,然后计算并显示它们的总和。使用循环控制结构,程序必须验证数据输入,并在用户输入任何非正值时显示错误信息。
Solution
这个练习已经在练习 25.1-4 中讨论过了。这里唯一的区别是,这个程序必须验证数据输入,并在用户输入无效值时显示错误信息。为了方便起见,该练习中提出的解决方案如下。
total = 0
for i in range(4):
x = float(input("Enter a number: ")) [更多…]
total = total + x
print(total)
本练习的主要目的是演示如何将验证数据输入的循环控制结构嵌套到其他现有的循环控制结构中。在这个练习中,你应该将带有虚线矩形的语句替换为以下代码片段
x = float(input("Enter a number: "))
while x <= 0:
print("Please enter a positive value!")
x = float(input("Enter a number: "))
最终的 Python 程序变为
file_29.3-2
total = 0
for i in range(4):
x = float(input("Enter a number: ")) [更多…]
while x <= 0:
print("Please enter a positive value!")
x = float(input("Enter a number: "))
total = total + x
print(total)
注意,替换的代码片段完全嵌套在这个外层 for 循环中。
29.4 使用循环控制结构查找最小和最大值
在第 22.2 节中,你学习了如何使用单选择决策结构在四个值中查找最小和最大值。现在,以下代码片段实现了相同的结果,但只使用了一个变量 w,用于用户提供的值。
w = int(input()) #用户输入第 1 个值
最大值 = w
w = int(input()) #用户输入第 2 个值
if w > maximum:
最大值 = w
w = int(input()) #用户输入第 3 个值
if w > maximum:
最大值 = w
w = int(input()) #用户输入第 4 个值
if w > maximum:
最大值 = w
除了第一对语句外,所有其他语句块都是相同的。因此,你可以保留这些对中的一对,并将其包含在一个执行三次迭代的循环控制结构中,如下所示。
w = int(input()) #用户输入第 1 个值
最大值 = w
for i in range(3):
w = int(input()) #用户输入第 2 个、第 3 个和第 4 个值
if w > maximum:
最大值 = w
当然,如果你想允许用户输入更多的值,你可以简单地增加 for 循环的 final_value。
因此,下面展示了一个程序,该程序查找并显示 10 个人中体重最重的人。
file_29.4a
w = int(input("输入体重(以磅为单位):"))
最大值 = w
for i in range(9):
w = int(input("输入体重(以磅为单位):"))
if w > maximum:
最大值 = w
print(maximum)
注意,for 循环迭代的次数比用户提供的值的总数少一次。
尽管这个 Python 程序运行良好,但让我们做一些稍微不同的事情。而不是在循环之前提示用户输入第一个值,然后在循环中输入剩余的九个值,让我们在循环中提示他们输入所有值。
然而,这里出现的问题是,无论如何,在循环开始迭代之前,必须始终给变量最大值分配一个初始值。但是,这个值不能随意选择;它取决于给定的问题。因此,选择一个“几乎随意的”初始值需要仔细考虑,因为错误的选择可能会导致不准确的结果。
在这个练习中,所有用户提供的值都与人们的体重有关。由于在地球上不可能找到任何体重为负的人(至少不是在地球上),你可以安全地将初始值-1 分配给变量最大值,如下所示。
file_29.4b
最大值 = -1
for i in range(10):
w = int(input("输入体重(以磅为单位):"))
if w > maximum:
最大值 = w
print(maximum)
一旦执行流程进入循环,用户输入第一个值,决策控制结构评估为 True。然后,变量 maximum 中的初始值-1 被这个第一个用户提供的值覆盖,之后执行流程正常进行。
请注意,这种方法可能不适用于所有情况。如果练习要求提示用户输入任何数字(不仅限于正数),则不能应用此方法,因为用户可能会只输入负值。如果发生这种情况,初始值-1 将永远不会被用户提供的任何值替换。当用户提供的值的下限已知时,可以使用此方法找到最大值;或者当用户提供的值的上限已知时,可以找到最小值。例如,如果练习要求找到最轻的人,可以将变量 minimum 的初始值设置为+1500,因为地球上没有人能重达如此之多!为了参考,乔恩·布朗·米诺奇是一位美国人,在他体重最高的时候,被记录为有史以来最重的人类,体重约为 1400 磅!!!!!
练习 29.4-1 验证并找到最小值和最大值
编写一个 Python 程序,提示用户输入 10 个人的体重,然后找出最轻和最重的体重。使用循环控制结构,程序还必须验证数据输入,并在用户输入任何非正值或任何大于 1500 的值时显示错误信息。
解答
使用上一个练习作为指导,你现在应该能够闭着眼睛完成这个任务!
为了验证数据输入,你只需要替换上一个练习中的以下代码行,
w = int(input("请输入一个体重(以磅为单位):"))
使用以下代码片段:
w = int(input("请输入一个介于 1 到 1500(以磅为单位)之间的重量:"))
while w < 1 or x > 1500:
w = int(input("无效值!请输入一个介于 1 到 1500(以磅为单位)之间的重量:"))
以下是一个找到最轻和最重体重的最终程序。
file_29.4-1
minimum = 1500
maximum = 0
for i in range(10):
w = int(input("请输入一个介于 1 到 1500(以磅为单位)之间的重量:")) [更多…]
while w < 1 or w > 1500:
w = int(input("无效值!请输入一个介于 1 到 1500(以磅为单位)之间的重量:"))
if w < minimum:
minimum = w
if w > maximum:
maximum = w
print(minimum, maximum)
练习 29.4-2 验证并找到最热的行星
编写一个 Python 程序,提示用户反复输入来自太空的行星名称和平均温度,直到输入“STOP”(用作名称)为止。最后,程序必须显示最热行星的名称。此外,由于-459.67^o(华氏温度)是可能达到的最低温度(称为绝对零度),程序还必须验证数据输入(使用循环控制结构),并在用户输入低于绝对零度的温度值时显示错误信息。
解决方案
首先,让我们编写一个不使用数据验证的 Python 程序。根据“终极”规则,预测试循环结构应如下所示,以一般形式给出:
name = input("输入行星名称: ") #名称初始化
while name.upper() != "STOP":
一个语句或语句块
name = input("输入行星名称: ") #名称更新/修改
现在,让我们添加其余的语句,仍然不进行数据输入验证。请注意,由于值-459.67^o 是温度尺度的下限,你可以使用低于这个值的值作为变量 maximum 的初始值。
maximum = -460
mName = ""
name = input("输入行星名称: ")
while name.upper() != "STOP":
t = float(input("输入其平均温度: "))
if t > maximum:
maximum = t
mName = name
name = input("输入行星名称: ")
if maximum != -460:
print("最热的行星是:", mName)
else:
print("未输入任何内容!")
需要 if maximum != -460 语句,因为用户有可能从一开始就输入“STOP”这个词。
为了验证数据输入,你只需替换以下代码行:
t = float(input("输入其平均温度: "))
with the following code fragment:
t = float(input("输入其平均温度: "))
while t < -459.67:
t = float(input("无效值!输入其平均温度: "))
最终程序如下。
file_29.4-2
maximum = -460
mName = ""
name = input("输入行星名称: ")
while name.upper() != "STOP":
t = float(input("输入其平均温度: "))
while t < -459.67:
t = float(input("无效值!输入其平均温度: "))
if t > maximum:
maximum = t
mName = name
name = input("输入行星名称: ")
if maximum != -460:
print("最热的行星是:", mName)
else:
print("未输入任何内容!")
练习 29.4-3 “获得高分”
在教室里,有 20 名学生。编写一个 Python 程序,提示教师输入学生在数学考试中获得的分数(0 至 100 分),然后显示最高分以及获得“A”(即 90 至 100 分)的学生人数。此外,程序必须验证数据输入。用户提供的值必须在 0 至 100 分的范围内。
解决方案
首先,我们先编写一个没有数据验证的程序。由于学生人数已知,可以使用 for 循环。对于变量 maximum 的初始值,可以使用值-1,因为没有低于 0 的分数。
maximum = -1
count = 0
for i in range(20):
grade = int(input("学生编号" + str(i + 1) + "的分数: "))
if grade > maximum:
maximum = grade
if grade >= 90:
count += 1
print(maximum, count)
现在,你可以处理数据验证。根据练习的措辞,不需要显示任何错误信息。所以,你需要做的就是替换以下代码行:
grade = int(input("学生编号" + str(i + 1) + "的分数: "))
使用以下代码片段:
while True:
grade = int(input("学生编号" + str(i + 1) + "的分数: "))
if 0 <= grade <= 100: break
最终程序变为
file_29.4-3
maximum = -1
count = 0
for i in range(20):
while True:
grade = int(input("学生编号" + str(i + 1) + "的分数: "))
if 0 <= grade <= 100: break
if grade > maximum:
maximum = grade
if grade >= 90:
count += 1
print(maximum, count)
29.5 使用循环控制结构解决数学问题
练习 29.5-1 计算用户想要的任意多个三角形的面积
编写一个 Python 程序,提示用户输入三角形的三边长度 A、B 和 C,然后计算并显示其面积。你可以使用海伦公式,
.
其中 S 是半周长
.
程序必须根据用户的意愿迭代多次。每次面积计算结束后,程序必须询问用户是否想要计算另一个三角形的面积。如果答案是“是”,程序必须重复;否则必须结束。程序应接受所有可能的答案形式,如“是”、“YES”、“Yes”或甚至“YeS”。
此外,使用循环控制结构,程序必须验证数据输入,并在用户输入任何非正值时显示错误信息。
解决方案
根据“终极”规则,后测试循环结构应如下所示,以一般形式给出。
answer = "yes" #答案的初始化(冗余)。
while True:
提示用户输入三角形的三边长度 A、B、C,然后计算并显示其面积。
答案的更新/修改
answer = input("你想重复吗?")
if answer.upper() != "YES": break
upper()方法确保程序对用户提供的任何答案“是”、“yes”、“YES”或甚至“YeS”或“yEs”都能正常工作!
本练习的解决方案如下。
file_29.5-1
from math import sqrt
while True:
提示用户输入边长 A 的长度
a = float(input("输入边长 A: "))
while a <= 0:
a = float(input("无效的边长。请输入边长 A: "))
提示用户输入边长 B 的长度
b = float(input("输入边长 B: "))
while b <= 0:
b = float(input("无效的边长。请输入边长 B: "))
提示用户输入边长 C 的长度
c = float(input("输入边长 C: "))
while c <= 0:
c = float(input("无效的边长。请输入边长 C: "))
计算并显示三角形的面积
s = (a + b + c) / 2
area = sqrt(s * (s - a) * (s - b) * (s - c))
print("面积是:", area)
answer = input("你想重复吗?")
if answer.upper() 不等于 "YES": break
练习 29.5-2 求解 x 和 y
编写一个 Python 程序,显示在范围 -20 到 +20 内所有可能的整数 x 和 y 值,这些值验证以下公式:
3x² − 6y² = 6
解答
如果你只想显示变量 x 和 y 的所有可能组合,你可以使用以下 Python 程序。
for x in range(-20, 21):
for y in range(-20, 21):
print(x, y)
然而,从所有这些组合中,你只需要那些验证表达式 3x² − 6y² = 6 的组合。决策控制结构非常适合这个目的!最终的 Python 程序如下。
file_29.5-2
for x in range(-20, 21):
for y in range(-20, 21):
if 3 * x ** 2 - 6 * y ** 2 == 6:
print(x, y)
练习 29.5-3 俄罗斯乘法算法
你可以使用“俄罗斯乘法算法”乘以两个正整数,该算法在以下流程图中展示。

编写相应的 Python 程序并创建跟踪表以确定输入值 5 和 13 时每一步的变量值。
解答
在给定的流程图中,一个单分支决策结构嵌套在一个预测试循环结构中。相应的 Python 程序如下。
file_29.5-3
m1 = int(input())
m2 = int(input())
s = 0
当 m2 不等于 0 时:
if m2 % 2 不等于 0:
s += m1
m1 *= 2
m2 //= 2
print(s)
对于输入值 5 和 13,跟踪表如下所示。
| 步骤 | 语句 | 备注 | m1 | m2 | s |
|---|---|---|---|---|---|
| 1 | m1 = int(input()) | 用户输入值 5 | 5 | ? | ? |
| 2 | m2 = int(input()) | 用户输入值 13 | 5 | 13 | ? |
| 3 | s = 0 | 5 | 13 | 0 | |
| 4 | while m2 不等于 0: | 这将评估为 True | |||
| 5 | if m2 % 2 不等于 0: | 这将评估为 True | |||
| 6 | s += m1 | 5 | 13 | 5 | |
| 7 | m1 *= 2 | 10 | 13 | 5 | |
| 8 | m2 //= 2 | 10 | 6 | 5 | |
| 9 | while m2 不等于 0: | 这将评估为 True | |||
| 10 | if m2 % 2 不等于 0: | 这将评估为 False | |||
| 11 | m1 *= 2 | 20 | 6 | 5 | |
| 12 | m2 //= 2 | 20 | 3 | 5 | |
| 13 | while m2 不等于 0: | 这将评估为 True | |||
| 14 | if m2 % 2 不等于 0: | 这将评估为 True | |||
| 15 | s += m1 | 20 | 3 | 25 | |
| 16 | m1 *= 2 | 40 | 3 | 25 | |
| 17 | m2 //= 2 | 40 | 1 | 25 | |
| 18 | while m2 不等于 0: | 这将评估为 True | |||
| 19 | if m2 % 2 不等于 0: | 这将评估为 True | |||
| 20 | s += m1 | 40 | 1 | 65 | |
| 21 | m1 *= 2 | 80 | 1 | 65 | |
| 22 | m2 //= 2 | 80 | 0 | 65 | |
| 23 | while m2 不等于 0: | 这将评估为 False | |||
| 24 | print(s) | 显示值 65,这是当然的,是 5 乘以 13 的结果 |
练习 29.5-4 求一个数的因数个数
编写一个 Python 程序,允许用户输入一个正整数,然后显示它的因数总数。
解答
让我们看看一些例子。
►值 12 的因数是数字 1,2,3,4,6,12。
►值 15 的因数是数字 1,3,5,15。
►值 20 的因数是数字 1,2,4,5,10,20。
►值 50 的因数是数字 1,2,5,10,25,50。
如果变量 x 包含用户提供的整数,x 的所有可能的因数都在 1 到 x 之间。因此,这里只需要一个 for 循环,变量 counter 的值从 1 到 x,在每次迭代中,一个简单的选择结构检查 counter 的值是否是 x 的因数。以下是一个 Python 程序。
file_29.5-4a
x = int(input())
numberOfDivisors = 0
for i in range(1, x + 1):
if x % i == 0:
numberOfDivisors += 1
print(numberOfDivisors)
这个程序对于输入值 20,执行了 20 次迭代。然而,如果它能够执行少于一半的迭代并达到相同的结果会更好!当然,这样会更好!所以,让我们让它更高效!
如你所知,对于任何用户提供的整数(变量 x)
►值 1 总是因数。
►用户提供的整数总是它自己的因数。
►除了用户提供的整数外,在 1 到 x 的范围中间没有其他因数。
因此,对于任何整数,肯定有 2 个因数,值 1 和用户提供的整数本身。因此,程序必须从值 2 开始检查其他可能的因数,直到 1 到 x 的范围中间。改进后的 Python 程序如下。
file_29.5-4b
x = int(input())
numberOfDivisors = 2
for i in range(2, x // 2 + 1):
if x % i == 0:
numberOfDivisors += 1
print(numberOfDivisors)
这个 Python 程序执行的迭代次数比上一个程序少!例如,对于输入值 20,这个 Python 程序只执行了(20 - 2) DIV 2 = 9 次迭代!
练习 29.5-5 判断一个数是否为素数?
编写一个 Python 程序,提示用户输入一个大于 1 的整数,然后显示一条消息,指出该数是否为素数。素数是任何大于 1 的整数,它除了 1 和它本身外没有其他因数。数字 7,11 和 13 都是这样的数。
解答
这个练习基于上一个练习。它非常简单!如果用户提供的整数只有两个因数(1 和它本身),则该数是素数。以下是一个 Python 程序。
file_29.5-5a
x = int(input("请输入一个大于 1 的整数:"))
numberOfDivisors = 2
for i in range(2, x // 2 + 1):
if x % i == 0:
numberOfDivisors += 1
if numberOfDivisors == 2:
print("数字", x, "是素数")
现在让我们使程序更高效。当找到第三个除数时,执行流程可以跳出循环,因为这意味着用户提供的整数肯定不是素数。Python 程序如下。
file_29.5-5b
x = int(input("Enter an integer greater than 1: "))
numberOfDivisors = 2
for i in range(2, x // 2 + 1):
if x % i == 0:
numberOfDivisors += 1
break
if numberOfDivisors == 2:
print("Number", x, "is prime")
练习 29.5-6 从 1 到 N 找到所有素数
编写一个 Python 程序,提示用户输入一个大于 1 的整数,然后显示从 1 到该用户提供的整数的所有素数。使用循环控制结构,程序必须验证数据输入,并在用户输入任何小于 1 的值时显示错误信息。
解答
以下 Python 程序以一般形式给出,解决了这个练习。
主代码
N = int(input("Enter an integer greater than 1: "))
while N <= 1:
N = int(input("Wrong number. Enter an integer greater than 1: "))
for x in range(1, N + 1):
代码片段 1:检查变量 x 是否包含一个素数
代码片段 1,如下所示,取自之前的练习(练习 29.5-5)。它检查变量 x 是否包含一个素数。
代码片段 1
numberOfDivisors = 2
for i in range(2, x // 2 + 1):
if x % i == 0:
numberOfDivisors += 1
break
if numberOfDivisors == 2:
print("Number", x, "is prime")
在将代码片段 1 嵌入主代码后,最终的 Python 程序如下
file_29.5-6
N = int(input("Enter an integer greater than 1: "))
while N <= 1:
N = int(input("Wrong number. Enter an integer greater than 1: "))
for x in range(1, N + 1):
numberOfDivisors = 2 [更多…]
for i in range(2, x // 2 + 1):
if x % i == 0:
numberOfDivisors += 1
break
if numberOfDivisors == 2:
print("Number", x, "is prime")
练习 29.5-7 赫伦平方根
编写一个 Python 程序,提示用户输入一个非负值,然后使用赫伦公式计算其平方根,如下所示。
.
其中
►y 是你想要找到平方根的数
►x[n] 是 y 的平方根的第 n 次迭代值
此外,使用循环控制结构,程序必须验证数据输入,并在用户输入任何负值时显示错误信息。
解答
你现在可能有点困惑,正在挠头。不要被所有这些数学东西吓到!你可以通过以下流程图来尝试理解赫伦公式!

仍然感到困惑?让我们通过一个例子来解释。让我们尝试找到 25 的平方根:
►提出一个猜测。假设 8 作为你的第一个猜测。
►8 的平方是 64。
►由于 64 不够接近 25,通过计算表达式来制定一个新的猜测

►5.56 的平方大约是 30.91
►由于 30.91 不够接近 25,通过计算表达式来制定一个新的猜测
.
►5.02 的平方是 25.2
►如果你认为 25.2 足够接近 25,那么你可以停止整个过程,并得出结论,25 的近似平方根是 5.02。
显然,如果需要更高的精度,你可以继续这个过程,直到找到一个被认为是更接近 25 平方根的值。
现在,让我们看看相应的 Python 程序。
file_29.5-7
from random import randrange
ACCURACY = 0.0000000000001
y = float(input("请输入一个非负数:"))
while y < 0:
y = float(input("无效值。请输入一个非负数:"))
guess = randrange(1, y + 1) #在 1 和用户提供的值之间生成一个随机初始猜测
while abs(guess ** 2 - y) > ACCURACY: #它“足够接近”了吗?
guess = (guess + y / guess) / 2 #不,创建一个新的“猜测”!
print(guess)
注意检查“它足够接近”的方式。当差的绝对值 |guess² - y| 变得小于 0.0000000000001(其中 y 是用户提供的值)时,执行流程退出循环。
练习 29.5-8 计算π
编写一个 Python 程序,使用以下 Madhava–Leibniz 级数计算π,精度为 0.00001。
.
解答
Madhava–Leibniz 级数可以解出π,并变为
.
分数越多,精度越好!因此,为了计算这个公式,程序需要执行多次迭代,以便尽可能多地使用分数。但是,当然,它不能无限迭代!循环必须在当前计算出的π值和前一次迭代计算出的π值“足够接近”时停止迭代,这意味着它们之间的差的绝对值已经变得非常小。常量 ACCURACY 定义了这种差异必须有多小。Python 程序如下所示。
file_29.5-8
精度 = 0.00001
pi = 0
sign = 1 #这是第一个分数的符号
denom = 1 #这是第一个分数的分母
while True:
piPrevious = pi #保留前一个 pi 值
pi += sign * 4 / denom #通过添加一个分数(项)来计算新的π
sign = -sign #为下一个分数准备符号
denom += 2 #为下一个分数准备分母
if abs(pi - piPrevious) <= ACCURACY: break #它“足够接近”了吗?
print("π ~=", pi)
注意变量 sign 在每次迭代之间在-1 和+1 之间切换的方式。
如果你降低常数 ACCURACY 的值,π 将会计算得越来越精确。根据你的计算机速度,你可以相当快地计算出 π 的前五个数字。然而,计算 π 的后续每一位所需的时间会呈指数级增长。使用这种方法在现代计算机上计算 40 位 π 可能需要数年!
练习 29.5-9 使用分数逼近实数
编写一个 Python 程序,提示用户输入一个介于 0 和 100 之间的实数,然后尝试找到更好的近似该实数的分数
,其中 N 是介于 0 和 100 之间的整数,M 是介于 1 和 100 之间的整数。使用循环控制结构,程序还必须验证数据输入,只允许用户输入介于 0 和 100 之间的值。无需显示任何错误消息。
解答
解答很简单。你所需要做的就是迭代所有可能的 n 和 m 的组合,并检查哪一个更好地逼近用户提供的实数。
要迭代 n 和 m 所有可能的组合,你可以使用嵌套循环控制结构,即两个嵌套的 for 循环,如下所示。
for n in range(101):
for m in range(1, 101):
…
总迭代次数为 101 × 100 = 10100。这是一个相当大的数字,但对于现代计算机来说,这只是小菜一碟!
变量 m 代表分数的分母,分母不能为零。这就是为什么它从 1 开始,而不是从 0 开始。
以下标准
.
可以评估近似的好坏。
感到困惑?让我们尝试用分数逼近值 0.333,通过迭代 N 和 M 所有可能的组合。
►对于 N = 1,M = 1 时,标准等于
= 0.6670
►对于 N = 1,M = 2 时,标准等于
= 0.1670
►对于 N = 1,M = 3 时,标准等于
= 0.0003
►对于 N = 1,M = 4 时,标准等于
= 0.0830
►…
►对于 N = 100,M = 99 时,标准等于
= 0.6771
►对于 N = 100,M = 100 时,标准等于
= 0.6670
显然,0.0003 是所有可能结果中的最小值。因此,N = 1 和 M = 3(对应于分数 1/3)的组合被认为是 0.333 的最佳近似。
现在的 Python 程序:
file_29.5-9
while True:
x = float(input("Enter a real between 0 and 100: "))
if 0 <= x <= 100: break
minimum = 100
for n in range(101):
for m in range(1, 101):
y = abs(n / m - x)
if y < minimum:
minimum = y
bestN = n
bestM = m
print("The fraction is:", bestN, "/", bestM)
29.6 使用循环控制结构的通用练习
练习 29.6-1 华氏度到开尔文,从 0 到 100
编写一个 Python 程序,显示从 0 到 100 华氏度的所有华氏度和它们对应的开氏度。使用增量值为 0.5。已知
1.8 · 开氏度 = 华氏度 + 459.67
解决方案
将公式解为开氏度,得到
.
这里你需要一个 while-loop,它使用步长 0.5 将变量 fahrenheit 从 0 增加到 100。解决方案如下。
file_29.6-1a
fahrenheit = 0
while fahrenheit <= 100:
kelvin = (fahrenheit + 459.67) / 1.8
print("Fahrenheit:", fahrenheit, "Kelvin:", kelvin)
fahrenheit += 0.5
当然,你现在可能想知道这个练习是否可以用 for-loop 解决。有人可能会说“不,这是不可能的!range() 函数的步长必须是整数!”然而,有一个简单的技巧可以帮助你使用 for-loop 解决这个练习,如下所示。
file_29.6-1b
for f in range(0, 1001, 5):
fahrenheit = f / 10
kelvin = (fahrenheit + 459.67) / 1.8
print("Fahrenheit:", fahrenheit, "Kelvin:", kelvin)
一般来说,除法和乘法是占用 CPU 时间的操作。尽管第二种方法效果不错,但并不是最佳选择。for-loop 中包含的语句 fahrenheit = f / 10 可能看起来无害,但实际上并非如此,因为除法 f / 10 被执行了 1001 次!这就像有一个包含 1001 个语句的序列控制结构,每个语句执行 1001 次除法!尽可能避免在循环中执行除法和乘法操作!
练习 29.6-2 棋盘上的米粒
关于一个发明了国际象棋的穷人的传说。印度的国王对这款新游戏非常满意,以至于他提出给这个穷人任何他想要的。这个贫穷但聪明的男人告诉他的国王,他想要棋盘第一格一粒米,第二格两粒,第三格四粒,以此类推,每个棋盘的 64 个方格都是翻倍。国王认为这是一个适度的要求,于是命令他的仆人带来大米。
编写一个 Python 程序,计算并显示棋盘上最终会有多少粒米,以及多少磅米。假设一磅米含有大约 30,000 粒米。
解决方案
假设一个只有 2 × 2 = 4 个方格的国际象棋棋盘,以及一个变量 grains 被赋予初始值 1(这是第 1 个方格的米粒数量)。一个循环三次的 for-loop 可以在每次迭代中将变量 grains 的值翻倍,如下代码片段所示。
grains = 1
for i in range(3):
grains = 2 * grains
每次迭代结束时变量 grains 的值如下表所示。
| 迭代 | grains 的值 |
|---|---|
| 第 1 次迭代 | 2 × 1 = 2 |
| 第 2 次迭代 | 2 × 2 = 4 |
| 第 3 次迭代 | 2 × 4 = 8 |
在第 3 次迭代的末尾,变量 grains 的值为 8。这个值不是棋盘上的总米粒数,而只是第 4 个方格上的米粒数。如果你需要找到棋盘上的总米粒数,你可以将所有方格上的米粒数相加,即 1 + 2 + 4 + 8 = 15。
在现实世界中,一个真正的棋盘包含 8 × 8 = 64 个方格,因此你需要迭代 63 次。以下是一个 Python 程序。
file_29.6-2
grains = 1
total = 1
for i in range(63):
grains = 2 * grains
total += grains # 这相当于 total = total + grains
weight = total / 30000
print(total, weight)
如果你想知道这些数字有多大,这里就是答案:在棋盘上将有 18,446,744,073,709,551,615 粒米;即 614,891,469,123,651.8 磅!
Exercise 29.6-3 Just a Poll
一家民意调查公司询问 1000 名公民他们是否在早上吃早餐。编写一个 Python 程序,提示公民输入他们的性别(M 代表男性,F 代表女性,O 代表其他)以及他们对问题的回答(Y 代表是,N 代表否,S 代表有时),然后计算并显示回答“是”的公民数量,以及回答“否”的女性公民的百分比。使用循环控制结构,程序还必须验证数据输入,并且只接受性别为 M、F 或 O,以及回答为 Y、N 或 S 的值。
Solution
以下是一个 Python 程序。
file_29.6-3a
CITIZENS = 1000
totalYes = 0
femaleNo = 0
for i in range(CITIZENS):
while True:
gender = input("Enter gender: ").lower()
if gender == "m" or gender == "f" or gender == "o": break
while True:
answer = input("Do you eat breakfast in the morning? ").lower()
if answer == "y" or answer == "n" or answer == "s": break
if answer == "y":
totalYes += 1
if gender == "f" and answer == "n":
femaleNo += 1
print(totalYes)
print(femaleNo * 100 / CITIZENS, "%", sep = "")
注意 Python 如何将用户的输入转换为小写。
当然,经过一点改进,这个程序可以变成
file_29.6-3b
CITIZENS = 1000
totalYes = 0
femaleNo = 0
for i in range(CITIZENS):
while True:
gender = input("Enter gender: ").lower()
if gender in ["m", "f", "o"]: break
while True:
answer = input("Do you eat breakfast in the morning? ").lower()
if answer in ["y", "n", "s"]: break
if answer == "y":
totalYes += 1
elif gender == "f":
femaleNo += 1
print(totalYes)
print(femaleNo * 100 / CITIZENS, "%", sep = "")
Exercise 29.6-4 Is the Message a Palindrome?
回文是一个既可以正向读也可以反向读的单词或句子。(你可能还记得,从练习 22.5-4 中,一个数字也可以是回文)。编写一个 Python 程序,提示用户输入一个单词或句子,然后显示一条消息,说明用户提供的单词或句子是否是回文。以下是一些回文单词和消息。
Anna
Radar
Madam
一颗坚果换一罐金枪鱼罐头。
Dennis 和 Edna 犯了罪。
借一罐红酒换命。
借还是抢?
我们不是被吸引向前,我们这些人,被吸引向前进入新时代?
解决方案
在开始逐个比较字母并检查第一个字母是否与最后一个字母相同,第二个字母是否与倒数第二个字母相同等等之前,有一些事情你应该记住。
在给定的句子或单词中,一些字母可能是大写,一些可能是小写。例如,在句子“A nut for a jar of tuna”中,尽管第一个和最后一个字母相同,但它们不被认为是相等的。因此,程序必须首先将所有字母(例如,转换为小写)进行比较,然后才能开始比较它们。
删除空格、句号、问号和逗号等字符对于程序准确比较字母至关重要。例如,如果不进行此步骤,在句子“借还是抢?”中,程序会错误地假设它不是一个回文,因为它会尝试将初始字母“B”与最后的问号“?”进行比较。
假设要检查的句子是“借还是抢?”。在将所有字母转换为小写并删除所有不需要的空格和问号之后,句子变为“borroworrob”。这些字母及其在字符串中的对应位置如下:

你在这里应该意识到的是,for 循环应该只遍历字母的一半。你能想出为什么吗?
程序应该开始迭代,并将位置 0 的字母与位置 10 的字母进行比较。然后它应该将位置 1 的字母与位置 9 的字母进行比较,以此类推。最后一次迭代应该是比较位置 4 和 6 的字母。在此之后继续检查将是徒劳的,因为所有字母都已经比较过了。
这个问题有多个解决方案。下面介绍其中一些。程序中的注释可以帮助你完全理解它们的工作方式。然而,如果你仍然对它们的工作方式有疑问,可以使用 IDE(IDLE 或 Visual Studio Code)逐步执行它们并观察每一步的变量值。
第一种方法
解决方案在此处展示。
file_29.6-4a
message = input("输入一条消息: ").lower()
创建一个新的字符串,其中包含除空格、逗号、句号和问号之外的所有内容
messageClean = ""
for letter in message:
if letter != " " and letter != "," and letter != "." and letter != "?":
messageClean += letter
j = len(messageClean) – 1 # 这是 messageClean 的最后一个位置
middlePos = j // 2 # 这是 messageClean 的中间位置
palindrome = True # 开始时,假设句子是回文
此循环逐个比较字母。
for i in range(middlePos + 1):
leftLetter = messageClean[i]
rightLetter = messageClean[j]
如果至少有一对字母未能通过验证,将变量 palindrome 设置为 False
if leftLetter != rightLetter:
palindrome = False
j -= 1
如果变量 palindrome 仍然为 True
if palindrome:
print("信息是回文")
第二种方法
前一种方法效果良好,但假设用户输入一个非常长的句子,它不是回文;例如,它的第二个字母与倒数第二个字母不同。不幸的是,在前一种方法中,最后一个 for 循环即使在变量 palindrome 被设置为 False 后,仍然继续迭代到句子的中间。所以,让我们尝试使这个程序更加完善。如您所知,您可以使用 break 语句在循环完成所有迭代之前跳出循环。
此外,您可以通过两种方式改进前一种方法:
►您可以使用 not in 成员运算符从变量 message 中移除空格、逗号、句号和问号。
►而不是使用变量 j 访问 messageClean 变量右侧的字母,你可以使用负索引。
解决方案在此处展示。
file_29.6-4b
message = input("输入一条信息: ").lower()
使用 "not in" 成员运算符移除空格、逗号、句号和问号
messageClean = ""
for letter in message:
if letter not in " ,.?":
messageClean += letter
middlePos = (len(messageClean) - 1) // 2
palindrome = True
for i in range(middlePos + 1):
if messageClean[i] != messageClean[-i - 1]: # 正确的字母使用负索引
palindrome = False
break
if palindrome:
print("信息是回文")
第三种方法
一种更 Pythonic 和复杂的方法是使用 replace() 方法移除空格、逗号、句号和问号,然后只需比较 messageClean 与其反转值。
解决方案在此处展示。
file_29.6-4c
message = input("输入一条信息: ").lower()
创建一个新的字符串,其中包含不带空格、逗号、句号或问号的字母
messageClean = message
for c in " ,.?":
messageClean = messageClean.replace(c, "")
if messageClean == messageClean[::-1]:
print("信息是回文")
此外,由于只需移除四种不同的字符(空格、逗号、句号和问号),您可以通过链式四个 replace() 方法来避免第一个循环,就像下面的 Python 程序所示。
file_29.6-4d
message = input("输入一条消息:").lower()
创建一个不包含空格、逗号、句号或问号的新字符串
messageClean = message.replace(" ", "").replace(",", "").replace(".", "").replace("?", "")
if messageClean == messageClean[::-1]:
print("该消息是回文")
很明显,一个问题可以有多个解决方案。取决于你找到最优解!
如果你希望删除所有不需要的字符(空格、逗号、句号、问号、和号等),你可以使用以下代码片段代替。它只保留变量 messageClean 中的字母。
创建一个只包含字母的新字符串
messageClean = ""
有效字符 = "abcdefghijklmnopqrstuvwxyz"
for 字符 in 消息:
if 字符 in 有效字符:
messageClean += 字符 #连接
29.7 复习问题:对/错
对以下每个陈述选择对/错。
1)数据验证是限制数据输入的过程,强制用户只输入有效值。
2)你可以使用确定循环来验证数据输入。
3)为了强制用户只输入正数,而不显示任何错误信息,你可以使用以下代码片段。
while True:
x = float(input("输入一个正数:"))
if x > 0: break
4)为了强制用户输入介于 1 和 10 之间的数字,你可以使用以下代码片段。
x = float(input("输入一个介于 1 和 10 之间的数字:"))
while x 大于等于 1 且 x 小于等于 10:
print("错误的数字")
x = float(input("输入一个介于 1 和 10 之间的数字:"))
5)为了找到 10 个用户提供的数字中的最小数,你可以使用以下代码片段。
最小值 = 0
for i in range(10):
w = float(input())
if w < 最小值:最小值 = w
6)为了找到 10 个用户提供的数字中的最大数,你可以使用以下代码片段。
最大值 = 0
for i in range(10):
w = float(input())
if w > 最大值:最大值 = w
7)为了找到 10 个用户提供的正数中的最大数,你可以使用以下代码片段。
最大值 = 0
for i in range(10):
w = float(input())
如果 w 大于最大值:最大值 = w
29.8 复习练习
完成以下练习。
1)设计一个流程图并编写相应的 Python 程序,提示用户反复输入非负值,直到它们的平均值超过 3000。最后,程序必须显示输入的总零数。
2)编写一个 Python 程序,提示用户输入一个介于 1 和 20 之间的整数,然后显示所有四位数,其各位数字之和小于用户提供的整数。例如,如果用户输入 15,则 9301 是这样的数字,因为
9 + 3 + 0 + 1 < 15
3)编写一个 Python 程序,显示所有满足以下条件的四位数:
►该数字的第一个数字大于第二个数字
►该数字的第二个数字等于第三个数字
►该数字的第三个数字小于第四个数字
例如,7559、3112 和 9889 是这样的数字。
4)编写一个 Python 程序,提示用户输入一个整数,然后显示其位数。
5)一名学生编写了以下代码片段,目的是验证数据输入,强制用户只能输入 0 和 1 的值。请识别代码片段中的任何错误。
while x != 1 or x != 0:
print("Error")
x = int(input())
6)使用循环控制结构,编写验证数据输入的代码片段,强制用户输入有效的性别(M 代表男性,F 代表女性,O 代表其他)。此外,它必须正确验证小写和大写字母。
7)编写一个 Python 程序,提示用户输入一个非负数,然后计算其平方根。使用循环控制结构,程序还必须验证数据输入,并在用户输入任何负值时显示错误信息。此外,用户有两次重试的机会。如果用户输入超过三个负值,必须显示消息“Dude, you are dumb!”,并且程序执行必须结束。
8)圆的面积可以使用以下公式计算:
面积 = π∙半径²
编写一个 Python 程序,提示用户输入圆的半径长度,然后计算并显示其面积。程序必须根据用户的意愿迭代执行。在每次面积计算结束后,程序必须询问用户是否希望计算另一个圆的面积。如果答案是“是”,程序必须重复执行;否则必须结束。程序应接受所有可能的答案形式,如“是”、“YES”、“Yes”或甚至“YeS”。
此外,使用循环控制结构,程序必须验证数据输入,并在用户输入任何非正值作为半径时显示错误信息。
提示:使用 math 模块中的 pi 获取π的值。
9)编写一个 Python 程序,提示用户输入 8 月份每天同一小时记录的温度(华氏度),然后计算并显示平均值以及最高温度。
由于-459.67°F(华氏温度)是可能达到的最低温度(称为绝对零度),使用循环控制结构,程序还必须验证数据输入,并在用户输入低于绝对零度的值时显示错误信息。
10)一名科学家需要一个软件应用程序来根据特定时间(HH:MM)记录的海平面值来记录海平面,以便提取一些有用的信息。编写一个 Python 程序,让科学家可以重复输入海平面值,包括小时和分钟,直到输入海平面值为 9999。然后,程序必须显示记录的最高和最低海平面值,以及记录这些水平值对应的小时和分钟。
11)在一些国家,当有人打喷嚏时,另一个人会大声说出一个数字(一个整数)。打喷嚏的人然后将这个数字的各位数相加,直到得到一个介于 1 到 26 之间的数字。这个数字对应的字母(1 对应“A”,2 对应“B”,依此类推)代表可能正在想他们的人的名字的第一个字母。
编写一个 Python 程序,提示用户输入打喷嚏后所说的数字。然后必须将这个数字的各位数相加,直到得到一个介于 1 到 26 之间的数字,并显示对应的英文字母。
12)编写一个 Python 程序,显示所有在范围-100 到+100 之间满足以下公式的整数 x 和 y 的值:
5x + 3y² = 0
13)编写一个 Python 程序,显示所有在范围-10 到+10 之间满足以下公式的整数 x、y 和 z 的值:

14)编写一个 Python 程序,让用户输入三个正整数,然后使用俄罗斯乘法算法找到它们的乘积。
15)将练习 29.5-4 的 Python 程序重写,使用循环控制结构验证数据输入。如果用户输入非正整数,必须显示错误信息。
16)将练习 29.5-5 的 Python 程序重写,使用循环控制结构验证数据输入。如果用户输入小于或等于 1 的整数,必须显示错误信息。
17)编写一个 Python 程序,提示用户将两个正整数输入到变量 start 和 finish 中。然后程序必须找到并显示所有满足 x² + y² = z²的毕达哥拉斯三元组(x, y, z),其中 x、y 和 z 是介于 start 和 finish 之间的整数。
提示:为了使程序正确运行,独立于用户提供的哪个整数是最低的,你可以(如果需要)交换它们的值,使它们始终处于正确的顺序。
18)编写一个 Python 程序,提示用户输入两个正整数,然后显示它们之间所有的质整数。使用循环控制结构,程序还必须验证数据输入,并在用户输入小于+2 的值时显示错误信息。
提示:为了使程序正确运行,独立于用户提供的哪个整数是最低的,你可以(如果需要)交换它们的值,使它们始终处于正确的顺序。
19)完美数是一个正整数,它等于其所有正除数之和(不包括该数本身)。例如,6 的除数是 1、2 和 3(不包括 6 本身),且 1 + 2 + 3 = 6,使 6 成为一个完美数。编写一个 Python 程序,提示用户输入一个正整数,并显示一条消息,指出该数是否为完美数。使用循环控制结构,程序还必须验证数据输入,并在用户输入非正整数时显示错误信息。
20)编写一个 Python 程序,提示用户输入两个正整数,然后显示它们之间的所有完全数。使用循环控制结构,程序还必须验证数据输入,并在用户输入非正整数时显示错误信息。
提示:为了使你的程序正确运行,独立于用户提供的最低整数是哪个,你可以(如果需要)交换它们的值,使它们始终处于正确的顺序。
21)编写一个 Python 程序,提示用户输入两个正四位数整数,然后显示它们之间所有是回文的整数。使用循环控制结构,程序还必须验证数据输入,并在用户输入任何非四位数时显示错误信息。
提示:为了使你的 Python 程序正确运行,独立于用户提供的最低整数是哪个,你可以(如果需要)交换它们的值,使它们始终处于正确的顺序。
22)编写一个 Python 程序,显示从 1 字节到 1GByte 之间所有可能的 RAM 大小,例如 1, 2, 4, 8, 16, 32, 64, 128,等等。
提示:1GByte 等于 2³⁰字节,或 1073741824 字节
23)编写一个 Python 程序,显示以下数字序列:
1, 11, 23, 37, 53, 71, 91, 113, 137, … 401
24)编写一个 Python 程序,显示以下数字序列:
−1, 1, −2, 2, −3, 3, −4, 4, … −100, 100
25)编写一个 Python 程序,显示以下数字序列:
1, 11, 111, 1111, 11111, … 11111111
26)斐波那契数列是一个以下序列的数字序列:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, …
根据定义,前两项是 0 和 1,每一项都是前两项的和。
编写一个 Python 程序,允许用户输入一个正整数,然后显示与该用户提供的整数一样多的斐波那契数列项。
27)编写一个 Python 程序,允许用户输入一个正整数,然后显示所有小于该用户提供的整数的斐波那契数列项。
28)编写一个 Python 程序,提示用户输入一个正整数 N,然后根据以下公式找到并显示 y 的值:
.
此外,使用循环控制结构,当用户输入小于 1 的值时,程序必须验证数据输入并显示错误信息。
29)编写一个 Python 程序,提示用户输入一个正整数 N,然后根据以下公式找到并显示 y 的值
.
此外,使用循环控制结构,当用户输入非正值时,程序必须验证数据输入并显示错误信息。
30)编写一个 Python 程序,提示用户输入一个大于 2 的整数 N,然后根据以下公式找到并显示 y 的值:
.
此外,使用循环控制结构,程序必须验证数据输入,并在用户输入小于或等于 2 的值时显示错误信息。
提示:注意,在 1/3 的项之后,后续的分母递增 2。
31)编写一个 Python 程序,提示用户输入一个正整数 N,然后根据以下公式找到并显示 y 的值:
.
此外,使用循环控制结构,程序必须验证数据输入,并在用户输入非正值时显示错误信息。
32)在数学中,非负整数 N 的阶乘是所有小于或等于 N 的正整数的乘积,用 N!表示。0 的阶乘定义为等于 1。在数学中,你可以写成
.
例如,5 的阶乘写作 5!,等于 1 × 2 × 3 × 4 × 5 = 120。
编写一个 Python 程序,提示用户输入一个非负整数 N,然后计算它的阶乘。
33)编写一个 Python 程序,允许用户输入一个 x 的值,然后使用以下所示的泰勒级数(Taylor^([22]))计算并显示指数函数 e^x,精度为 0.00001。
.
提示:记住
。
34)编写一个 Python 程序,允许用户输入一个 x 的值,然后使用以下所示的泰勒级数计算并显示 x 的正弦值,精度为 0.00001。
.
提示:记住 x 是以弧度为单位的,并且
。
35)编写一个 Python 程序,允许用户输入一个 x 的值,然后使用以下所示的泰勒级数计算并显示 x 的余弦值,精度为 0.00001。
.
提示:记住 x 是以弧度为单位的,并且
。
36)假设字母 A 对应数字 1,字母 B 对应数字 2,依此类推。编写一个 Python 程序,提示用户输入两个整数,然后显示它们之间存在的所有字母。例如,如果用户输入 3 和 6,程序必须显示 C、D、E、F。使用循环控制结构,程序还必须验证数据输入,并在用户输入任何负数或任何大于 26 的值时显示不同的错误信息。
提示:为了使你的 Python 程序正确运行,独立于用户提供的整数哪个是最小的,你可以(如果需要)交换它们的值,使它们始终处于正确的顺序。
-
编写一个 Python 程序,随机选择一个介于 1 到 100 之间的整数并将其分配给一个变量。然后程序必须提示用户猜测这个数字。如果用户的猜测小于秘密数字,必须显示消息“你的猜测比我秘密数字小。再试一次!”如果用户的猜测大于秘密数字,必须显示消息“你的猜测比我秘密数字大。再试一次!”这个过程必须重复,直到用户正确猜出秘密数字。一旦用户猜对了,必须显示消息“你找到了!”以及用户所进行的总尝试次数。
-
通过使游戏适用于两位玩家来扩展之前的练习/游戏。获胜者是找到随机秘密数字尝试次数更少的玩家。
-
电视屏幕的大小始终指其对角线测量值。例如,一个 40 英寸的电视屏幕对角线长度为 40 英寸,从顶部的一个角到底部的另一个角。旧电视屏幕的宽高比为 4:3,这意味着对于电视屏幕高度中的每 3 英寸,电视屏幕宽度中有 4 英寸。如今,大多数电视屏幕的宽高比为 16:9,这意味着对于电视屏幕高度中的每 9 英寸,电视屏幕宽度中有 16 英寸。使用这些宽高比和勾股定理,你可以轻松地确定:
►对于所有 4:3 电视屏幕
宽度 = 对角线 × 0.8
高度 = 对角线 × 0.6
►对于所有 16:9 电视屏幕
宽度 = 对角线 × 0.87
高度 = 对角线 × 0.49
编写一个 Python 程序,显示以下菜单:
-
4/3 电视屏幕
-
16/9 电视屏幕
-
退出
并提示用户输入一个选择(1、2 或 3)以及英寸的屏幕对角线尺寸。然后,Python 程序必须显示电视屏幕的宽度和高度。这个过程必须反复进行,直到用户从菜单中选择 3(退出)。
- 编写一个 Python 程序,提示教师输入学生总数、他们的成绩和他们的性别(M 代表男性,F 代表女性,O 代表其他),然后计算并显示以下内容:
a) 获得“A”(90-100 分)的人的平均分
b) 获得“B”(80-89 分)的人的平均分
c) 获得“A”(90-100 分)的男生的平均分
d) 获得“B”以下成绩的女生的总数
e) 最高和最低成绩
f) 全班平均成绩
添加所有必要的检查,以确保程序满足确定性属性。此外,使用循环控制结构,程序必须验证数据输入,并在教师输入以下任何内容时显示错误消息:
►学生总数为非正数
►学生成绩为负数或大于 100 的值
►性别为除 M、F 或 O 之外的值
- 编写一个 Python 程序,根据以下表格计算并显示客户根据订单金额获得的折扣。
| 金额 | 折扣 |
|---|---|
| 0 < 金额 < 20 美元 | 0% |
| 20 美元 ≤ 金额 < 50 美元 | 3% |
| 50 美元 ≤ 金额 < 100 美元 | 5% |
| 100 美元 ≤ 金额 | 10% |
在每次折扣计算结束后,程序必须询问用户是否希望计算另一个金额的折扣。如果答案是“是”,程序必须重复;否则必须结束。程序应接受所有可能的形式的答案,如“是”、“YES”、“Yes”或甚至“YeS”。
此外,使用循环控制结构,程序必须验证数据输入,并在用户输入任何非正金额值时显示错误信息。
- LAV 电力公司根据以下表格(家庭账户的月度费率)向用户收取电费。
| 千瓦时 (kWh) | 每千瓦时 (kWh) 美元 |
|---|---|
| 0 ≤ 千瓦时 (kWh) ≤ 400 | 每千瓦时 (kWh) 0.11 美元 |
| 401 ≤ 千瓦时 (kWh) ≤ 1500 | 每千瓦时 (kWh) 0.22 美元 |
| 1501 ≤ 千瓦时 (kWh) ≤ 3500 | 每千瓦时 (kWh) 0.25 美元 |
| 3501 ≤ 千瓦时 (kWh) | 每千瓦时 (kWh) 0.50 美元 |
编写一个 Python 程序,提示用户输入用户消耗的总千瓦时 (kWh),然后计算并显示应付总额。此过程必须重复进行,直到输入的千瓦时值为 -1。
此外,使用循环控制结构,程序必须验证数据输入,并在用户输入任何负千瓦时 (kWh) 值时显示错误信息。对于值 -1 必须做出例外。
传输服务费和配电费,以及联邦、州和地方税,总计为每张账单增加 25%。
请注意,费率是累进制的。
第三十七章:在“循环控制结构”复习
复习填字游戏
1)解决以下填字游戏。

横向
4)这个控制结构允许多次执行一个语句块。
6)一个无法停止迭代的循环。
8)“终极”规则指出,参与循环布尔表达式的变量必须在进入循环之前 _________。
9)一个循环在另一个循环内部。
10)在这个循环结构中,迭代次数在循环开始迭代之前是未知的。
纵向
1)在一个 _______-测试循环结构中,首先评估布尔表达式,然后执行结构中的语句或语句块。
2)_______-测试循环至少执行一次迭代。
3)在这个循环结构中,迭代次数在循环开始迭代之前是已知的。
5)一个读起来前后相同的单词或句子。
7)任何大于 1 的整数,除了 1 和它自己没有其他除数。
复习问题
回答以下问题。
1)什么是循环控制结构?
2)在流程图中,你如何区分决策控制结构和循环控制结构?
3)设计流程图并编写一个 pre-test 循环结构的 Python 语句(一般形式)。解释这个循环控制结构是如何工作的。
4)为什么预测试循环结构会这样命名,它可能执行的最少迭代次数是多少?
5)如果一个预测试循环结构的语句或语句块执行了 N 次,那么这个结构的布尔表达式被评估了多少次?
6)设计流程图并编写一个 post-test 循环结构的相应 Python 语句(一般形式)。解释这个循环控制结构是如何工作的。
7)为什么后测试循环结构会这样命名,它可能执行的最少迭代次数是多少?
8)如果一个 post-test 循环结构的语句或语句块执行了 N 次,那么这个结构的布尔表达式被评估了多少次?
9)设计流程图并编写一个 mid-test 循环结构的相应 Python 语句(一般形式)。解释这个循环控制结构是如何工作的。
10)设计流程图并编写一个 for 循环的相应 Python 语句(一般形式)。解释这个循环控制结构是如何工作的。
11)说明适用于 for 循环的规则。
12)嵌套循环是什么?
13)编写一个使用嵌套循环控制结构的示例程序,并解释它们是如何执行的。
14)说明适用于嵌套循环的规则。
15)设计一个图表,可以帮助人们根据给定问题决定选择哪种循环控制结构。
16)描述“终极”规则,并给出两个示例,使用预测试和后测试循环结构的一般形式。
-
假设一个 Python 程序使用循环控制结构在电子英语词典中搜索一个给定的单词。为什么在找到给定单词时退出循环是至关重要的?
-
清理循环为什么是至关重要的?
-
什么是无限循环?
第六部分
Python 中的数据结构
第三十章
一维列表和字典
30.1 简介
变量是存储内存中的值的好方法,但它们有一个限制——一次只能存储一个值。然而,有许多情况下,程序需要将大量数据存储在内存中,而变量并不是最佳选择。
例如,考虑以下练习:
编写一个 Python 程序,让用户输入三个数字。然后按升序显示它们。
考虑以下代码片段。它允许用户输入三个数字。
for i in range(3):
number = float(input())
当循环最终完成迭代时,变量 number 只包含最后提供的那个数字。不幸的是,前两个数字已经丢失了!使用此代码片段,几乎不可能按升序显示它们。
一个可能的解决方案是使用三个单独的变量,如下所示。
num1 = float(input())
num2 = float(input())
num3 = float(input())
if num1 <= num2 <= num3:
print(num1, num2, num3)
elif num1 <= num3 <= num2:
print(num1, num3, num2)
elif num2 <= num1 <= num3:
print(num2, num1, num3)
elif num2 <= num3 <= num1:
print(num2, num3, num1)
elif num3 <= num1 <= num2:
print(num3, num1, num2)
else:
print(num3, num2, num1)
这不是一个完美的解决方案,但它有效!然而,如果这个练习的措辞要求用户输入 1000 个数字而不是三个呢?想想看!你能为所有这些数字编写类似的 Python 程序吗?当然不能!幸运的是,有数据结构!
在计算机科学中,数据结构是一组组织良好的数据,以便以最有效的方式对其执行操作。
Python 中有几种数据结构可用,如列表、元组、字典、集合、冻结集合和字符串。是的,你听对了!由于字符串是一组字母数字字符的集合,它被视为数据结构。
除了字符串(你已经学得足够多了)之外,列表和字典是 Python 中最常用的数据结构。以下章节将分析这两个。
30.2 列表是什么?
列表是一种数据结构类型,可以在一个共同的名称下存储多个值。它可以被视为一个元素集合,其中每个元素都分配了一个唯一的数字,称为索引位置,或简单地称为索引。列表是可变的(可更改的),这意味着一旦创建列表,其元素的值就可以更改,并且可以向列表中添加新元素或从列表中删除元素。
计算机科学中的列表类似于数学中使用的矩阵。数学矩阵是一组数字或其他数学对象,按行和列排列。
在许多计算机语言中,例如 Java、C++和 C#(仅举几个例子),没有列表。这些语言支持另一种称为“数组”的数据结构。然而,列表比数组更强大。
存在一维和多维列表。多维列表可以是二维的、三维的、四维的,等等。
一维列表
以下示例展示了一个一维列表,该列表包含了六个学生的成绩。列表的名称是 grades。为了方便起见,每个元素上对应着相应的索引。默认情况下,在 Python 中,索引编号总是从零开始。

由于索引编号从零开始,列表中最后一个元素的索引比列表中元素的总数少 1。在 grades 列表中,最后一个元素的索引是 5,而元素总数是 6。
你可以将列表想象成六个单独的变量——grades0、grades1、grades2、... grades5,每个变量都保存一个学生的成绩。然而,列表的优势在于它可以在一个共同的名称下保存多个值。
二维列表
通常,多维列表适用于处理多组数据。例如,假设你想要保存加利福尼亚州 4 月四周的每日最高气温。一种方法就是使用四个一维列表,每个列表代表一周。此外,每个列表将包含七个元素,每个元素代表一周中的某一天,如下所示。

然而,这种方法有点笨拙,因为你必须分别处理每个列表。更好的方法是将一个二维列表使用四行(每行代表一周)和七列(每列代表一周中的某一天),如下所示。

三维列表
下一个示例展示了一个三维列表,该列表包含了 2013 年和 2014 年 4 月四周内加利福尼亚州的每日最高气温。

请注意,四维、五维甚至一百维的列表都可以存在。然而,经验表明,作为程序员,你一生中可能需要的最大列表维度可能是两维或三维。
练习 30.2-1 设计列表
设计一个可以保存 8 个人年龄的列表,并向列表中添加一些典型值。
解答
这是一个简单的问题。你所要做的就是设计一个包含 8 个元素(索引 0 到 7)的列表。它可以是一个只有一行或一列的列表,如下所示。

然而,请记住,Python 中没有一行或一列的列表。这些概念可能存在于数学矩阵中(或者在你的想象中!)但不在 Python 中。Python 中的列表是一维的——这就是全部!如果你想要可视化它们有一行或一列,那取决于你。
练习 30.2-2 设计列表
设计必要的列表来存储七个人的姓名和年龄,然后向列表中添加一些典型值。
解答
这个练习可以用两个列表来实现。让我们设计它们,每个列表有一列。

如您所见,列表名称中的元素与列表年龄中的元素之间存在一一对应关系。这七个人中的第一个是约翰·汤普森,他 17 岁。名称“约翰·汤普森”存储在列表名称的索引 0 处,而在列表年龄的相同索引处存储了他的年龄。下一个人的姓名(艾娃·米勒)和她的年龄(25 岁)分别存储在列表名称和年龄的索引 1 处,以此类推。
练习 30.2-3 设计列表
设计必要的列表来存储十个人的姓名以及每个人一月份、二月份和三月份的平均体重(以磅为单位)。然后向列表中添加一些典型值。
解答
在这个练习中,你需要一个一维列表来存储姓名,以及一个二维列表来存储人们的体重,它们的元素之间存在一一对应关系。

30.3 在 Python 中创建一维列表
Python 有许多创建列表并向其中添加元素(和值)的方法。根据给定的问题,取决于你选择哪一种。
让我们尝试使用最常见的方法来创建以下列表。

第一种方法
要创建一个列表并直接向其元素分配值,你可以使用以下 Python 语句,以通用形式给出。
list_name = [ value0, value1, value2, …, valueM ]
其中
►list_name 是列表的名称。
►value0, value1, value2, … , valueM 是列表元素的值。
对于这种方法,你可以使用以下语句来创建列表 ages:
ages = [12, 25, 9, 11]
索引是自动设置的。值 12 被分配给索引位置 0 的元素,值 25 被分配给索引位置 1 的元素,依此类推。默认情况下,索引编号总是从零开始。
在第 5.4 节中,你学习了在给变量命名时必须遵循的规则。给列表命名遵循完全相同的规则!
第二种方法
你可以使用以下通用形式的语句在 Python 中创建一个大小为空元素的列表:
list_name = [None] * size
其中 size 可以是任何正整数值,甚至可以是包含任何正整数值的变量。
下一个语句创建了包含 4 个空元素的列表 ages。
ages = [None] * 4
语句 ages = [None] * 4 在主内存(RAM)中保留了四个位置。
要将值赋给列表元素,你可以使用以下语句,以一般形式给出:
list_name[index] = value
其中 index 是列表中元素的索引位置。
以下代码片段创建列表 ages(在主内存中保留四个位置),然后为其元素赋值。
ages = [None] * 4
ages[0] = 12
ages[1] = 25
ages[2] = 9
ages[3] = 11
列表 ages 的大小是 4。
当然,除了使用常量值作为索引外,你还可以使用变量或表达式,如下所示。
ages = [None] * 4
k = 0
ages[k] = 12
ages[k + 1] = 25
ages[k + 2] = 9
ages[k + 3] = 11
第三种方法
在这种方法中,你可以创建一个完全空的列表(没有元素),然后使用 append()方法向其中添加元素(和值),如下面的 Python 语句所示,以一般形式给出。
list_name = []
list_name.append(value0)
list_name.append(value1)
list_name.append(value2)
…
list_name.append(valueM)
使用这种方法,你可以使用以下代码片段创建名为 ages 的列表:
ages = []
ages.append(12)
ages.append(25)
ages.append(9)
ages.append(11)
请注意,在这种方法中,默认情况下索引编号从零开始,这意味着值 12 存储在索引位置 0,值 25 存储在索引位置 1,依此类推。
语句 ages = []不会在主内存(RAM)中保留任何位置。它只是声明列表 ages 已准备好接受新元素。
30.4 如何从一个一维列表中获取值
从列表中获取值只是指向一个特定元素的问题。一维列表中的每个元素都可以使用索引唯一标识。以下代码片段创建了一个列表,并在屏幕上显示“A+”(不带双引号)。
grades = ["B+", "A+", "A", "C-"]
print(grades[1])
当然,除了使用常量值作为索引外,你还可以使用变量或表达式。以下示例创建了一个列表,并在屏幕上显示“Aphrodite and Hera”(不带双引号)。
gods = ["Zeus", "Ares", "Hera", "Aphrodite", "Hermes"]
k = 2
print(gods[k + 1], "and", gods[k])
一个负索引从列表的末尾开始计数,访问一个元素。在以下列表 grades 中,每个元素(使用负索引)的索引位置如下。

以下示例
grades = ["B+", "A+", "A", "C-"]
print(grades[-1] , "and", grades[-3])
在屏幕上显示“C− and A+”(不带双引号)。
如果你希望显示列表中的所有元素,你可以这样做
grades = ["B+", "A+", "A", "C-"]
print(grades) # 显示: ['B+', 'A+', 'A', 'C-']
在 Python 中,你可以使用单引号或双引号定义字符串。
就像在字符串中一样,你可以获取列表的子集,称为“切片”,如下所示。
grades = ["B+", "A+", "A", "C-"]
print(grades[1:3]) #It displays: ["A+", "A"]
Python 中的切片是一种从列表(或一般序列)中选择元素范围的机制。
切片机制还可以有一个第三个参数,称为步长,如下所示。
grades = ["B+", "A+", "A", "C-", "A-", "B-", "C", "B", "C+"]
print(grades[1:7:2]) #It displays: ['A+', 'C-', 'B-']
负步长返回列表的逆序子集
gods = ["Ares", "Hera", "Aphrodite", "Hermes"]
print(gods[2:0:-1]) #It displays: ['Aphrodite', 'Hera']
print(gods[::-1]) #It displays: ['Hermes', 'Aphrodite', 'Hera', 'Ares']
切片机制使用以下记法
list[[beginIndex] : [endIndex] [: step]]
and returns a portion of list. Specifically, it returns the sublist starting from position beginIndex and running up to, but not including, position endIndex or up to the end of list, whichever comes first. Both arguments beginIndex and endIndex are optional. If beginIndex is omitted, the sublist starting from position 0 and running up to, but not including, position endIndex is returned. If endIndex is omitted, the sublist starting from position beginIndex until the end of list is returned. The last argument step is optional as well. If omitted, its default value is 1. If supplied, it defines the number of elements you want to move forward after each element is retrieved from the original list.
练习 30.4-1 创建跟踪表
Create the trace table for the next code fragment.
a = [None] * 4
a[3] = 9
x = 0
a[x] = a[3] + 4
a[x + 1] = a[x] * 3
x += 1
a[x + 2] = a[x - 1]
a[2] = a[1] + 5
a[3] = a[3] + 1
Solution
不要忘记,你可以像操作变量一样操作列表中的每个元素。因此,当你为使用列表的 Python 程序创建跟踪表时,你可以为每个元素设置一列,如下所示。
| 步骤 | 语句 | 备注 | x | a[0] | a[1] | a[2] | a[3] |
|---|---|---|---|---|---|---|---|
| 1 | a = [None] * 4 | This creates list a with no values in it | ? | ? | ? | ? | ? |
| 2 | a[3] = 9 | ? | ? | ? | ? | 9 | |
| 3 | x = 0 | 0 | ? | ? | ? | 9 | |
| 4 | a[x] = a[3] + 4 | 0 | 13 | ? | ? | 9 | |
| 5 | a[x + 1] = a[x] * 3 | 0 | 13 | 39 | ? | 9 | |
| 6 | x += 1 | 1 | 13 | 39 | ? | 9 | |
| 7 | a[x + 2] = a[x − 1] | 1 | 13 | 39 | ? | 13 | |
| 8 | a[2] = a[1] + 5 | 1 | 13 | 39 | 44 | 13 | |
| 9 | a[3] = a[3] + 1 | 1 | 13 | 39 | 44 | 14 |
练习 30.4-2 使用不存在的索引
以下 Python 程序不满足算法的哪些属性?
grades = ["B+", "A+", "A", "C-"]
print(grades[100])
Solution
该 Python 程序不满足两个属性。第一个属性很明显:没有数据输入。第二个属性是确定性属性。你永远不要引用列表中不存在的元素。在这个练习中,由于索引位置 100 没有元素,最后一个语句抛出了运行时错误。
30.5 如何更改列表元素的值
修改现有列表元素的值轻而易举。你只需要使用适当的索引并将新值赋给该元素。下面的例子正好展示了这一点。
创建一个列表
tribes = ["Navajo", "Cherokee", "Sioux"]
print(tribes) # 它显示:['Navajo', 'Cherokee', 'Sioux']
修改现有元素的值
tribes[1] = "Apache"
print(tribes) # 它显示:['Navajo', 'Apache', 'Sioux']
30.6 如何遍历一维列表
现在是有趣的部分。一个程序可以使用循环控制结构(通常是 for 循环)遍历列表的元素。你可以使用两种方法遍历一维列表。
第一种方法
这种方法通过索引引用每个列表元素。以下是一个通用形式的代码片段
for index in range(size):
process structure_name[index]
在其中,process 是每次迭代处理列表结构 _name 的一个 Python 语句或语句块。
以下 Python 程序在每次迭代中显示列表 gods 的所有元素。
gods = ["Zeus", "Ares", "Hera", "Aphrodite", "Hermes"]
for i in range(5):
print(gods[i])
i 变量的名称不是绑定性的。你可以使用任何你想要的变量名,比如 index、ind、j 等等。
注意,由于列表 gods 包含五个元素,for 循环必须从 0 迭代到 4,而不是从 1 迭代到 5。这是因为四个元素的索引分别是 0、1、2、3 和 4。
由于列表是可变的,你可以使用循环控制结构来修改其所有或部分值。以下代码片段将列表 b 中某些元素的值翻倍。
b = [80, 65, 60, 72, 30, 40]
for i in range(3):
b[i] = b[i] * 2
第二种方法
这种方法很简单,但不如前一种灵活。在某些情况下,它不能使用,你将在下面看到。以下是一个通用形式的代码片段
for element in structure_name:
process element
在其中,process 是每次迭代处理列表结构 _name 的一个 Python 语句或语句块。
以下 Python 程序,在每次迭代中显示列表 grades 的所有元素。
grades = ["B+", "A+", "A", "C-"]
for grade in grades:
print(grade)
在第一次迭代中,第一个元素的值被赋给变量 grade。在第二次迭代中,第二个元素的值被赋给变量 grade,依此类推!
以下 Python 程序以逆序显示列表 gods 的每个元素。
gods = ["Hera", "Zeus", "Ares", "Aphrodite", "Hermes"]
for god in gods[::-1]:
print(god)
但是,请记住,这种方法不能用来修改列表中元素的值。例如,如果你想将列表 numbers 中所有元素的值翻倍,你不能这样做:
numbers = [5, 10, 3, 2]
for number in numbers:
number = number * 2
number 是一个简单的变量,在每次迭代中,列表 numbers 的后续值被分配给它。然而,情况相反!number 的值永远不会分配回任何元素!
如果你想改变列表中元素的价值,你应该使用第一种方法。
练习 30.6-1 求和
编写一个 Python 程序,创建一个包含以下值的列表
56, 12, 33, 8, 3, 2, 98
然后计算并显示它们的总和。
解决方案
你学习了两种遍历列表元素的方法。让我们使用这两种方法并看看它们的区别。下面你会发现一个额外的第三种方法,这是计算一维列表元素总和的 Pythonic 方式。
第一种方法
解决方案如下。
file_30.6-1a
values = [56, 12, 33, 8, 3, 2, 98]
total = 0
for i in range(7):
total += values[i] #这相当于 total = total + values[i]
print(total)
第二种方法
解决方案如下。
file_30.6-1b
values = [56, 12, 33, 8, 3, 2, 98]
total = 0
for value in values:
total += value
print(total)
第三种方法
这种方法不使用任何循环结构。它只是使用了 math 模块中的 fsum()函数。
file_30.6-1c
from math import fsum
values = [56, 12, 33, 8, 3, 2, 98]
total = fsum(values)
print(total)
如果你对 fsum()函数没有任何印象,可以通过重新阅读第 11.2 节来刷新你的记忆。
30.7 如何将用户输入的值添加到一维列表中
这里没有什么新的内容。不是从键盘读取一个值并将其分配给一个变量,你可以直接将那个值分配给特定的列表元素。下一个代码片段提示用户输入四个人的姓名,并将它们分配给 names 列表的索引位置 0、1、2 和 3。
names = [None] * 4 #在主存(RAM)中预保留 4 个位置
names[0] = input("输入姓名编号 1: ")
names[1] = input("输入姓名编号 2: ")
names[2] = input("输入姓名编号 3: ")
names[3] = input("输入姓名编号 4: ")
使用 for 循环,这段代码可以等价地写成
ELEMENTS = 4
names = [None] * ELEMENTS #在主存(RAM)中预保留 4 个位置
for i in range(ELEMENTS):
names[i] = input("输入姓名编号 " + str(i + 1) + ": ")
你当然也可以这样做,使用 append()方法代替,如下面的代码片段所示。
ELEMENTS = 4
names = [] #创建一个完全空的列表
for i in range(ELEMENTS):
name = input("输入姓名编号 " + str(i + 1) + ": ")
names.append(name)
当使用 append()方法时,元素会被添加到列表中(添加到列表的末尾)。
处理列表大小的一个非常好的策略是使用常量。然而,与一些其他编程语言(如 C#或 C++)不同,Python 本身不支持常量。相反,你可以使用变量来实现类似的结果。不过,建议只使用大写字母。这有助于你从视觉上区分用作常量的变量和普通变量。
练习 30.7-1 以逆序显示单词
编写一个 Python 程序,让用户输入 20 个单词。程序必须然后以它们提供的顺序的逆序显示它们。
解决方案
列表非常适合这类问题。以下是一个合适的解决方案。
file_30.7-1a
words = [None] * 20 #在主内存(RAM)中预保留 20 个位置
for i in range(20):
words[i] = input()
for i in range(19, -1, -1):
print(words[i])
由于索引编号从零开始,列表中最后一个元素的索引比列表中元素的总数少 1。
请记住,在 Python 中,你可以使用切片机制和步长为-1 来以逆序遍历列表元素。以下程序创建了一个完全空的列表,然后使用 append()方法向列表中添加元素。最后,使用切片机制以它们提供的顺序的相反顺序显示它们。
file_30.7-1b
words = [] #创建一个完全空的列表
for i in range(20):
words.append(input())
for word in words[::-1]:
print(word)
有时练习的措辞可能没有提到使用数据结构。然而,这并不意味着你不能使用它。当你发现需要时,请使用数据结构(列表、元组、字典等)。
一个“元组”几乎与列表相同。主要区别是元组是不可变的(不可更改的)。
一个“不可变”的数据结构是在数据结构创建后其元素值不能改变的结构。显然,你无法向不可变数据结构中添加新元素或从其中删除现有元素。
练习 30.7-2 以逆序显示正数
编写一个 Python 程序,让用户输入 100 个数字到一个列表中。然后,它只显示提供的顺序的逆序中的正数。
解决方案
在这个练习中,程序必须接受用户的所有值并将它们存储到一个列表中。然而,在负责显示列表元素的 for 循环中,一个嵌套的决策控制结构必须检查并显示仅有的正数。解决方案如下。
file_30.7-2
ELEMENTS = 100
values = [None] * ELEMENTS
for i in range(ELEMENTS):
values[i] = float(input())
for value in values[::-1]:
if value > 0:
print(value)
与一些其他编程语言(如 C#或 C++)不同,Python 本身不支持常量。相反,您可以使用变量来实现类似的结果。不过,建议只使用大写字母。这有助于您在视觉上区分用作常量的变量和普通变量。
练习 30.7-3 查找平均值
编写一个 Python 程序,提示用户输入 20 个数字到一个列表中。然后,只有在它们的平均值小于 10 时才显示一条消息。
解答
为了找到用户提供的数字的平均值,程序必须首先找到它们的总和,然后将该总和除以 20。一旦找到平均值,程序必须检查是否显示相应的消息。
file_30.7-3a
ELEMENTS = 20
values = [None] * ELEMENTS
for i in range(ELEMENTS):
values[i] = float(input("Enter a value: "))
Accumulate values in total
total = 0
for i in range(ELEMENTS):
total += values[i]
average = total / ELEMENTS
if average < 10:
print("Average value is less than 10")
如果你想知道这个练习是否可以用一个循环解决,答案是“可以”。下面将提供一个替代解决方案。
file_30.7-3b
ELEMENTS = 20
total = 0
values = [None] * ELEMENTS
for i in range(ELEMENTS):
values[i] = float(input("Enter a value: "))
total += values[i]
average = total / ELEMENTS
if average < 10:
print("Average value is less than 10")
但让我们澄清一下!尽管许多过程可以在一个循环中完成,但将每个单独的过程在一个单独的循环中执行会更简单。这可能不是那么高效,但既然你还是一个新手程序员,试着先采用这种编程风格。以后,当你有了经验并成为 Python 大师时,你将能够“合并”一个循环中的许多过程!
现在,让我们看看使用 fsum()函数的更 Pythonic 的方法。
file_30.7-3c
from math import fsum
ELEMENTS = 20
values = []
for i in range(ELEMENTS):
values.append(float(input("Enter a value: ")))
if fsum(values) / ELEMENTS < 10:
print("Average value is less than 10")
练习 30.7-4 仅显示实数
编写一个 Python 程序,提示用户输入 10 个数值到一个列表中。然后,程序必须显示包含实数的元素的索引。
解答
在练习 22.1-1 中,你学习了如何检查一个数是否为整数。相应地,要检查一个数是否为实数(浮点数),可以使用布尔表达式
number != int(number)
解决方案如下。
file_30.7-4
ELEMENTS = 10
b = [None] * ELEMENTS
for i in range(ELEMENTS):
b[i] = float(input("Enter a value for element " + str(i) + ": "))
for i in range(ELEMENTS):
if b[i] != int(b[i]):
print("A real found at index:", i)
练习 30.7-5 显示奇数索引的元素
编写一个 Python 程序,提示用户将 8 个数值输入到一个列表中。然后程序必须显示奇数索引的元素(即索引 1、3、5 和 7)。
解答
以下是一个可能的解决方案。
file_30.7-5a
ELEMENTS = 8
values = [None] * ELEMENTS
for i in range(ELEMENTS):
values[i] = float(input("请输入元素 " + str(i) + " 的值: "))
显示奇数索引的元素
for i in range(ELEMENTS):
if i % 2 != 0:
print(values[i])
然而,你知道只有奇数索引位置的值需要显示。因此,负责显示列表元素的 for 循环,而不是从 0 开始计数并使用步长+1,可以从 1 开始计数并使用步长+2。这种修改将迭代次数减半。修改后的 Python 程序如下。
file_30.7-5b
ELEMENTS = 8
values = [None] * ELEMENTS
for i in range(ELEMENTS):
values[i] = float(input("请输入元素 " + str(i) + " 的值: "))
显示奇数索引的元素
for i in range(1, ELEMENTS, 2): # 从 1 开始,每次增加 2
print(values[i])
如前所述,在 Python 中,你可以使用切片机制遍历列表元素。在以下程序中,切片机制用于仅显示奇数索引的元素。
file_30.7-5c
ELEMENTS = 8
values = [None] * ELEMENTS
for i in range(ELEMENTS):
values[i] = float(input("请输入元素 " + str(i) + " 的值: "))
显示奇数索引的元素
for value in values[1::2]: # 从 1 开始,每次增加 2
print(value)
练习 30.7-6 在奇数索引位置显示偶数
编写一个 Python 程序,允许用户输入 100 个整数到一个列表中,然后显示存储在奇数索引位置的任何偶数值。
解答
以下是一个可能的解决方案。
file_30.7-6a
ELEMENTS = 100
values = [None] * ELEMENTS
for i in range(ELEMENTS):
values[i] = int(input())
for i in range(1, ELEMENTS, 2): # 从 1 开始,每次增加 2
if values[i] % 2 == 0:
print(values[i])
然而,一种更 Pythonic 的方法是使用此处所示的切片机制。
file_30.7-6b
ELEMENTS = 100
values = []
for i in range(ELEMENTS):
values.append(int(input()))
for value in values[1::2]:
if value % 2 == 0:
print(value)
30.8 关于连接和重复操作符的更多内容
在第 7.5 节中,你学习了如何使用字符串运算符(+)和()相应地连接和重复字符串。相同的运算符以及它们对应的赋值运算符(+=)和(=)也可以用于列表。更具体地说,连接运算符(+)和连接赋值运算符(+=)可以用来连接两个(或更多)列表,而重复运算符()和重复赋值运算符(=)可以用来重复相同的列表多次。让我们看一些例子。
以下代码片段创建列表 x,最终包含的值[1, 2, 3, 4, 5, 6]。
a = [1, 2]
b = [3, 4]
x = a + b # 列表 x 包含的值[1, 2, 3, 4]
x += [5, 6] # 列表 x 包含的值[1, 2, 3, 4, 5, 6]
你已经在第 30.3 节中使用了重复运算符(*)来创建一个使用以下一般形式给出的语句的空元素列表:
list_name = [None] * size
这个语句实际上所做的就是重复一个只有一个空元素(这是大小为[None]的列表)的列表大小次。例如,以下代码片段迭代列表[None] 3 次。
x = [None] * 3 # 列表 x 包含的值[None, None, None]
以下代码片段创建列表 x,最终包含的值[5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6]。
x = [5, 6] * 2 # 列表 x 包含的值[5, 6, 5, 6]
x *= 3 # 列表 x 包含的值[5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6]
30.9 什么是字典?
在计算机科学中,字典和列表之间的主要区别在于,字典元素可以通过键唯一标识,而不一定是整数值。字典的每个键都与一个元素相关联(或者如果你愿意,可以映射)。字典的键可以是字符串、整数、浮点数或元组。
以下示例展示了一个包含家庭成员名称的字典。字典的名称是 family,相应的键写在每个元素上方。

字典元素的键必须在字典内是唯一的。这意味着在字典家族中,例如,你不能有两个名为 father 的键。
字典元素的值可以是任何类型。
30.10 在 Python 中创建字典
让我们尝试使用最常见的方法来创建以下字典。

首次方法
要创建一个字典并直接为其元素赋值,可以使用以下 Python 语句,以一般形式给出。
dict_name = {key0: value0, key1: value1, key2: value2, …, keyM: valueM }
其中
►dict_name 是字典的名称。
►key0, key1, key2, … , keyM 是字典元素的键。
►value0, value1, value2, … , valueM 是字典元素的值。
使用这种方法,可以使用以下语句创建字典 pupil:
pupil = {"firstName": "Ann", "lastName": "Fox", "age": 8, "class": "2nd"}
每个键与其值之间由冒号(:)分隔,元素之间由逗号分隔,所有内容都包含在大括号({})内。
在第 5.4 节中,你学习了在给变量命名时必须遵循的规则。给字典命名遵循完全相同的规则!
第二种方法
在这种方法中,你可以使用以下语句创建一个完全空的字典,如一般形式所示。
dict_name = {}
然后在以下 Python 语句中添加一个元素(键值对),如所示,以一般形式给出。
dict_name[key] = value
使用这种方法,可以使用以下代码片段创建 pupil 字典:
pupil = {}
pupil["firstName"] = "Ann"
pupil["lastName"] = "Fox"
pupil["age"] = 8
pupil["class"] = "2nd"
30.11 如何从字典中获取值
要获取特定字典元素的值,你必须使用相应的键指向该元素。以下代码片段创建了一个字典,然后在屏幕上显示“Ares is the God of War”(不包含双引号)。
olympians = {
"Zeus": "King of the Gods",
"Hera": "Goddess of Marriage",
"Ares": "God of War",
"Poseidon": "God of the Sea",
"Demeter": "Goddess of the Harvest",
"Artemis": "Goddess of the Hunt",
"Apollo": "God of Music and Medicine",
"Aphrodite": "Goddess of Love and Beauty",
"Hermes": "Messenger of the Gods",
"Athena": "Goddess of Wisdom",
"Hephaistos": "God of Fire and the Forge",
"Dionysus": "God of the Wine"
}
print("Ares is the", olympians["Ares"])
只能使用键来访问元素。这意味着 olympians["Ares"]正确返回“God of War”,但 olympians["God of War"]无法返回“Ares”。
练习 30.11-1 罗马数字转数字
罗马数字如下表所示。
| Number | Roman Numeral |
|---|---|
| 1 | I |
| 2 | II |
| 3 | III |
| 4 | IV |
| 5 | V |
编写一个 Python 程序,提示用户输入一个介于 I 和 V 之间的罗马数字,然后显示相应的数字。假设用户输入的是有效值。
解决方案
显然,解决方案将是使用类似于以下代码片段的多重选择决策结构。
if romanNumeral == "I"
number = 1
elif romanNumeral == "II"
number = 2
elif romanNumeral == "III"
number = 3
elif romanNumeral == "IV"
number = 4
elif romanNumeral == "V"
number = 5
然而,这种方法相当冗长,如果你想要扩展你的程序以处理更多的罗马数字,它可能会变得更加复杂。因此,掌握了关于字典的知识后,你可以采用更有效的方法,如下面的代码片段所示。
roman2number = {"I": 1, "II": 2, "III": 3, "IV": 4, "V": 5}
number = roman2number[romanNumeral]
本练习的解决方案如下。
file_30.11-1
roman2number = {"I": 1, "II": 2, "III": 3, "IV": 4, "V": 5}
romanNumeral = input("Enter a Roman numeral: ")
number = roman2number[romanNumeral]
print(romanNumeral + ":", number)
练习 30.11-2 在字典中使用不存在的键
以下 Python 程序有什么问题?
family = {"father": "John", "mother": "Maria", "son": "George"}
print(family["daughter"])
解决方案
与列表类似,这段代码不满足确定性属性。你永远不要引用一个不存在的字典元素。由于没有键“daughter”,最后一行会抛出一个运行时错误。
30.12 如何修改字典元素的值
要修改现有字典元素的值,你需要使用适当的关键字并将新值赋给该元素。下面的例子正好展示了这一点。
创建一个字典
tribes = {"Indian": "Navajo", "African": "Zulu"}
print(tribes) #它显示: {'Indian': 'Navajo', 'African': 'Zulu'}
修改现有元素的值
tribes["Indian"] = "Apache"
print(tribes) #它显示: {'Indian': 'Apache', 'African': 'Zulu'}
练习 30.12-1 向不存在的键赋值
以下代码片段有什么问题?
tribes = {0: "Navajo", 1: "Cherokee", 2: "Sioux"}
tribes[3] = "Apache"
解决方案
不,这次代码片段中绝对没有任何错误。乍一看,你可能以为最后一行尝试修改一个不存在的键的值,这将引发错误。然而,对于 Python 字典来说并非如此。因为tribes是一个字典,键“3”不存在,所以最后一行向字典中添加了一个全新的第四个元素!
字典的键可以是字符串、整数、浮点数或元组。
虽然如此,但如果tribes实际上是一个列表,最后一行肯定会引发错误。看看下面的代码片段
tribes = ["Navajo", "Cherokee", "Sioux"]
tribes[3] = "Apache"
在这个例子中,由于tribes是一个列表,索引 3 不存在,所以最后一行尝试修改一个不存在的元素的值,显然会引发错误!
30.13 如何遍历字典
要遍历字典的元素,你可以使用 for 循环。实际上有两种方法。让我们研究一下这两种方法!
第一种方法
以下是一个通用形式的代码片段
for key in structure_name:
process structure_name[key]
在哪个过程中,任何 Python 语句或语句块都会在每次迭代中处理structure_name字典的一个元素。
以下 Python 程序显示了字母 A、B、C 和 D 及其对应的摩尔斯电码。
morseCode = {"A": ".-", "B": "-...", "C": "-.-.", "D": "-.."}
for letter in morseCode:
print(letter, morseCode[letter])
下一个示例给计算机软件公司的每位员工额外奖励$2000!
salaries = {
"项目经理": 83000,
"软件工程师": 81000,
"网络工程师": 64000,
"系统管理员": 61000,
"软件开发工程师": 70000
}
for title in salaries:
salaries[title] += 2000
第二种方法
以下是一个代码片段,以通用形式编写
for key, value in structure_name.items():
process key, value
在其中,任何 Python 语句或语句块都会在每次迭代中处理字典结构 structure_name 的一个元素。
以下 Python 程序显示字典 grades 的所有元素,每个迭代一个。
grades = {"John": "B+", "George": "A+", "Maria": "A", "Helen": "A-"}
for name, grade in grades.items():
print(name, "got", grade)
不幸的是,这种方法不能用来改变字典中元素值的值。例如,如果你想将字典 salaries 中所有元素的值加倍,你不能这样做:
salaries = {
"项目经理": 83000,
"软件工程师": 81000,
"网络工程师": 64000,
"系统管理员": 61000,
"软件开发工程师": 70000
}
for title, salary in salaries.items():
salary *= 2
salary 是一个简单的变量,在每次迭代中,字典 salaries 的后续值被分配给它。然而,相反的情况永远不会发生!salary 的值永远不会分配回任何元素!
如果你想改变字典中元素的值,你应该使用第一种方法。
30.14 关于成员运算符的更多内容
在第 15.5 节中,你学习了 Python 的成员运算符 in 和 not in。你同样可以使用这些运算符来评估操作数是否存在于指定的数据结构中。让我们看看一些示例。
以下示例显示“存在!”
x = [1, 2, 3, 4, 5, 6]
if 3 in x:
print("It exists!")
以下示例在列表 y 中查找“George”。
y = ["John", "Maria", "Anna", "George", "Tes"]
if "George" in y:
print("I found George!!!!")
注意,in 运算符检查操作数是否存在于数据结构中。它不能找到操作数被找到的索引位置!
以下示例提示用户输入一个名字,然后检查该名字是否不在列表 y 中。
y = ["John", "Maria", "Anna", "George", "Tes"]
name = input("Enter a name to search: ")
if name not in y:
print(name, "not found!!!!")
以下示例检查键“Son”是否存在于字典 family 中。
family = {"Father": "John", "Mother": "Maria", "Son": "George"}
if "Son" in family:
print("Key 'Son' found!")
30.15 复习问题:正确/错误
对以下每个陈述选择正确或错误。
1)列表是能够存储多个值的结构。
2)列表元素位于主存储器(RAM)中。
3)列表只能是单维或二维的。
4)不能有四维列表。
5)列表被称为“多维”是因为它可以包含不同类型的数据。
6)每个列表元素都有一个唯一的非负索引。
7)在字典中可以有重复的键。
8)在列表中,默认情况下索引编号从零开始。
9)最后一个列表元素的索引等于其元素的总数。
10)可以存在一个二维列表。
11)下一个语句包含语法错误。
studentNames = None * 10
12)在 Python 程序中,两个列表不能有相同的名称。
13)下一个语句在语法上是正确的。
student = {"firstName": "Ann" - "lastName": "Fox" - "age": 8}
14)在 Python 程序中,两个列表不能有相同数量的元素。
15)您不能在列表中使用变量作为索引。
16)您可以在列表中使用数学表达式作为索引。
17)您不能在字典中使用变量作为键。
18)以下代码片段不会抛出错误。
a = "a"
fruits = {"o": "Orange", "a": "Apple", "w": "Watermelon"}
print(fruits[a])
19)如果您在列表中使用变量作为索引,则此变量必须包含整数值。
20)为了计算 20 个用户提供的数值的总和,您必须使用列表。
21)您可以使用语句 b[k] = input()让用户将值输入到列表 b 中。
22)以下语句创建了一个包含两个空元素的二维列表。
names = [None] * 3
23)以下代码片段将值 10 赋给索引为 7 的元素。
values[5] = 7
values[values[5]] = 10
24)以下代码片段将值“Sally”赋给索引为 2 的元素,不使用双引号。
names = [None] * 3
names[2] = "John"
names[1] = "George"
names[0] = "Sally"
25)以下语句将值“Sally”赋给索引为 2 的元素,不使用双引号。
names = ["John", "George", "Sally"]
26)以下代码片段在屏幕上显示“Sally”,不使用双引号。
names = [None] * 3
k = 0
names[k] = "John"
k += 1
names[k] = "George"
k += 1
names[k] = "Sally"
k -= 1
print(names[k])
27)以下代码片段在语法上是正确的。
names = ["John", "George", "Sally"]
print(names[])
28)以下代码片段在屏幕上显示“Maria”,不使用双引号。
from math import pi
names = ["John", "George", "Sally", "Maria"]
print(names[int(pi)])
29)以下代码片段满足确定性的属性。
grades = ["B+", "A+", "A"]
print(grades[3])
30)以下代码片段满足确定性的属性。
v = [1, 3, 2, 9]
print(v[v[v[0]]])
31)以下代码片段在屏幕上显示值 1。
v = [1, 3, 2, 0]
print(v[v[v[v[0]]]])
32)以下代码片段显示列表 names 的所有元素。
names = ["John", "George", "Sally", "Maria"]
i = i
while i < 4:
print(names[i])
i += 1
33)以下代码片段满足确定性的属性。
names = ["John", "George", "Sally", "Maria"]
for i in range(2, 5):
print(names[i])
34)以下代码片段允许用户将 100 个值输入到列表 b 中。
for i in range(100):
b[i] = input()
35)如果列表 b 包含 30 个元素(算术值),以下代码片段加倍了所有元素的价值。
for i in range(29, -1, -1):
b[i] = b[i] * 2
36)可以使用 for 循环来加倍列表中某些元素的价值。
37)如果列表 b 包含 30 个元素,以下代码片段显示所有这些元素。
for element in b[0:29]:
print(element)
38)如果 b 是一个字典,以下代码片段显示其所有元素。
for key, element in b:
print(element)
39)以下代码片段会抛出错误。
fruits = {
"O": "Orange",
"A": "Apple",
"W": "Watermelon"
}
print(fruits["Orange"])
30.16 复习问题:多项选择题
选择以下每个陈述的正确答案。
1)以下语句
lastNames = [NONE] * 5
a)包含逻辑错误。
b)包含语法错误。
c)是一个正确的语句。
d)以上皆非
2)以下代码片段
x = 5
values[x / 2] = 10
a)不满足确定性的性质。
b)不满足有限性的性质。
c)不满足有效性的性质。
d)以上皆非
3)如果变量 x 包含值 4,以下语句
values[x + 1] = 5
a)将值 4 赋给索引为 5 的元素。
b)将值 5 赋给索引为 4 的元素。
c)将值 5 赋给索引为 5 的元素。
d)以上皆非
4)以下语句
values = []
values.append(5)
a)将值 5 赋给索引为 1 的元素。
b)将值 5 赋给索引为 0 的元素。
c)不满足确定性的性质。
d)以上皆非
5)以下代码片段
values[0] = 1
values[values[0]] = 2
values[values[1]] = 3
values[values[2]] = 4
a)将值 4 赋给索引为 3 的元素。
b)将值 3 赋给索引为 2 的元素。
c)将值 2 赋给索引为 1 的元素。
d)以上皆是
e)以上皆非
6)如果列表 values 包含数值,以下语句
print(values[values[1] − values[1 % 2]] − values[int(1/2)])
a)不满足确定性的性质。
b)总是显示 0。
c)总是显示 1。
d)以上皆非
7)你可以使用 for 循环迭代一维列表,该循环使用
a)变量 i 作为计数器。
b)变量 j 作为计数器。
c)使用变量 k 作为计数器。
d)任何变量作为计数器。
8)以下代码片段
names = ["George", "John", "Maria", "Sally"]
for i in range(3, 0, -1):
print(names[i])
a)按升序显示所有名称。
b)按升序显示一些名称。
c)按降序显示所有名称。
d)按降序显示一些名称。
e)以上皆非
9)以下代码片段
fruits = ["apple", "orange", "onion", "watermelon"]
print(fruits[1])
a)显示:“orange”
b)显示:“apple”
c)显示:“orange”
d)抛出错误,因为洋葱不是水果!
e)以上皆非
10)如果列表 b 包含 30 个元素(算术值),以下代码片段
for i in range(29, 0, -1):
b[i] = b[i] * 2
a)加倍了其某些元素的价值。
b)将其所有元素值加倍。
c)以上都不是
11)以下代码片段
struct = {"firstName": "George", "lastName": "Miles", "age": 28}
for a, b in struct.items():
print(b)
a)显示字典元素的键。
b)显示字典元素的值。
c)显示字典元素的键值对。
d)以上都不是
12)以下代码片段
struct = {"firstName": "George", "lastName": "Miles", "age": 28}
for x in struct:
print(x)
a)显示字典元素的键。
b)显示字典元素的值。
c)显示字典元素的键值对。
d)以上都不是
13)以下代码片段
tribes = {0: "Navajo", 1: "Cherokee", 2: "Sioux", 3: "Apache"}
for i in range(4):
print(tribes[i])
a)显示字典元素的键。
b)显示字典元素的值。
c)显示字典元素的键值对。
d)以上都不是
14)以下代码片段
tribes = {"tribeA": "Navajo", "tribeB": "Cherokee", "tribeC": "Sioux"}
for x in tribes:
tribes[x] = tribes[x].upper()
a)将字典元素的键全部转换为大写。
b)将字典元素的值全部转换为大写。
c)将字典元素的键值对全部转换为大写。
d)以上都不是
30.17 复习练习
完成以下练习。
1)设计一个数据结构来存储五个人的体重(以磅为单位),然后向结构中添加一些典型值。
2)设计必要的数据结构来存储七个人的姓名和体重(以磅为单位),然后向结构中添加一些典型值。
3)设计必要的数据结构来存储五个湖泊的名称以及六月份、七月份和八月份每个湖泊的平均面积(以平方英里为单位)。然后向结构中添加一些典型值。
4)设计一个数据结构来存储 10 个箱子的三个维度(宽度、高度和深度以英寸为单位)。然后向结构中添加一些典型值。
5)设计必要的数据结构来存储八个湖泊的名称以及每个湖泊的平均面积(以平方英里为单位)和最大深度(以英尺为单位)。然后向结构中添加一些典型值。
6)设计必要的数据结构来存储四个湖泊的名称以及它们在六月第一周、七月第一周和八月第一周的平均面积(以平方英里为单位)。
7)为以下代码片段创建跟踪表。
a = [None] * 3
a[2] = 1
x = 0
a[x + a[2]] = 4
a[x] = a[x + 1] * 4
8)为以下代码片段创建跟踪表。
a = [None] * 5
a[1] = 5
x = 0
a[x] = 4
a[a[0]] = a[x + 1] % 3
a[a[0] / 2] = 10
x += 2
a[x + 1] = a[x] + 9
9)为以下代码片段创建三个不同执行情况的跟踪表。
三次执行输入的值分别是:(i) 3, (ii) 4, 和 (iii) 1。
a = [None] * 4
a[1] = int(input())
x = 0
a[x] = 3
a[a[0]] = a[x + 1] % 2
a[a[0] % 2] = 10
x += 1
a[x + 1] = a[x] + 9
10)为以下代码片段创建跟踪表,针对三种不同的执行。
三次执行输入的值分别是:(i) 100,(ii) 108,和(iii) 1。
a = [None] * 4
a[1] = int(input())
x = 0
a[x] = 3
a[a[0]] = a[x + 1] % 10
if a[3] > 5:
a[a[0] % 2] = 9
x += 1
a[x + 1] = a[x] + 9
else:
a[2] = 3
11)在以下跟踪表中填写空缺。在第 6 步和第 7 步,填写一个变量的名称;对于所有其他情况,填写常量值、算术或比较运算符。
| 步骤 | 语句 | x | y | a[0] | a[1] | a[2] |
|---|---|---|---|---|---|---|
| 1 | a = [None] * 3 | ? | ? | ? | ? | ? |
| 2 | x = …… | 4 | ? | ? | ? | ? |
| 3 | y = x ‑ …… | 4 | 3 | ? | ? | ? |
| 4, 5 | if x …… y: a[0] = ……
else
a[0] = y | 4 | 3 | 1 | ? | ? |
| 6 | a[1] = …… + 3 | 4 | 3 | 1 | 7 | ? |
|---|---|---|---|---|---|---|
| 7 | y = …… ‑ 1 | 4 | 2 | 1 | 7 | ? |
| 8 | a[y] = (x + 5) …… 2 | 4 | 2 | 1 | 7 | 1 |
12)为以下代码片段创建跟踪表。
a = [17, 12, 45, 12, 12, 49]
for i in range(6):
if a[i] == 12:
a[i] -= 1
else:
a[i] += 1
13)为以下代码片段创建跟踪表。
a = [10, 15, 12, 23, 22, 19]
for i in range(1, 5):
a[i] = a[i + 1] + a[i - 1]
14)在不使用跟踪表的情况下,尝试确定以下代码片段执行时显示的值。
tribes = {
"印度-1": "纳瓦霍",
"印度-2": "切罗基",
"印度-3" : "苏族",
"非洲-1": "祖鲁",
"非洲-2": "马赛人",
"非洲-3": "约鲁巴"
}
for x, y in tribes.items():
if x[:6] == "印度":
print(y)
15)编写一个 Python 程序,允许用户输入 100 个数字到一个列表中,然后显示这些值的三次方。
16)编写一个 Python 程序,允许用户输入 80 个数字到一个列表中。然后,程序必须将列表中的值平方,并最终以与提供顺序相反的顺序显示它们。
17)编写一个 Python 程序,允许用户输入 90 个整数到一个列表中,然后显示那些正好能被 5 整除的数,并且以与提供顺序相反的顺序显示。
18)编写一个 Python 程序,允许用户输入 50 个整数到一个列表中,然后显示那些是偶数或大于 10 的数。
19)编写一个 Python 程序,允许用户输入 30 个数字到一个列表中,然后计算并显示那些是正数的总和。
20)编写一个 Python 程序,允许用户输入 50 个整数到一个列表中,然后计算并显示那些是两位数的总和。
提示:所有两位整数都在 10 到 99 之间。
21)编写一个 Python 程序,允许用户输入 40 个数字到一个列表中,然后计算并显示正数的总和以及负数的总和。
22)编写一个 Python 程序,允许用户输入 20 个数字到一个列表中,然后计算并显示它们的平均值。
23)编写一个 Python 程序,提示用户输入 50 个整数到列表中。然后显示包含小于 20 的值的元素的索引。
24)编写一个 Python 程序,提示用户输入 60 个数值到列表中。然后显示具有偶数索引的元素(即索引 0、2、4、6 等)。
25)编写一个 Python 程序,提示用户输入 20 个数值到列表中。然后计算并显示具有偶数索引的元素的总和。
26)编写一个 Python 代码片段,创建以下 100 个元素的列表。
.
27)编写一个 Python 代码片段,创建以下 100 个元素的列表。
.
28)编写一个 Python 程序,提示用户输入一个整数 N,然后创建并显示以下 N 个元素的列表。使用循环控制结构,程序还必须验证数据输入,并在用户输入任何小于 1 的值时显示错误消息。
.
29)编写一个 Python 程序,提示用户输入 10 个数值到列表中,然后显示包含整数的元素的索引。
30)编写一个 Python 程序,提示用户输入 50 个数值到列表中,然后计算并显示负元素的总量。
31)编写一个 Python 程序,提示用户输入 50 个单词到列表中,然后显示那些至少包含 10 个字符的单词。
提示:使用 len()函数。
32)编写一个 Python 程序,允许用户输入 30 个单词到列表中。然后显示那些少于 5 个字符的单词,然后是少于 10 个字符的单词,最后是少于 20 个字符的单词。
提示:尝试使用两个嵌套的 for 循环来显示单词。
33)编写一个 Python 程序,提示用户输入 40 个单词到列表中,然后显示那些至少包含字母“w”两次的单词。
34)以下表格显示了罗马数字。
| 数字 | 罗马数字(十位数字) | 罗马数字(个位数字) |
|---|---|---|
| 1 | X | I |
| 2 | XX | I |
| 3 | XXX | III |
| 4 | XL | IV |
| 5 | L | V |
| 6 | LX | VI |
| 7 | LXX | VII |
| 8 | LXXX | VIII |
| 9 | XC | IX |
每个罗马数字是十位和个位数字的组合。例如,数字 45 的罗马数字表示为 XLV(4 个十位由 XL 表示,5 个个位由 V 表示)。编写一个 Python 程序,提示用户输入一个介于 1 到 99 之间的整数,并显示相应的罗马数字。假设用户输入了一个有效的值。
提示:避免逐个检查每个整数,因为这需要 99 个备选决策结构的多个选择。尝试找到更有效和巧妙的方法!
第三十一章
二维列表
31.1 在 Python 中创建二维列表
二维列表是一种可以存储按行和列组织的数据的结构。它允许您有效地表示和操作表格数据。例如,可以存储三个学生四个课程成绩的列表如下。

二维列表有行和列。在这个特定的例子中,列表 grades 有 3 行和 4 列。
与一些其他编程语言不同,Python 不直接支持二维列表。Python 主要支持一维列表,但您可以使用一个技巧来解决这个问题并创建多维列表:您可以创建一个列表的列表!将 grades 列表想象成一个单列列表,有三个元素(每个学生一个),每个元素都包含一个完全新的列表,有四个元素(每个课程一个),如下所示。

在 Python 中,二维列表是列表的列表,三维列表是列表的列表的列表,依此类推。
与一维列表一样,有四种方法可以创建和向二维列表添加元素(及其值)。让我们尝试使用这些方法中的每一种来创建列表 grades。
首种方法
您可以使用以下一般形式的语句在 Python 中创建二维列表,
list_name = [None] * number_of_rows
其中
►list_name 是列表的名称。
►number_of_rows 可以是任何正整数。
然后,您可以使用以下一般形式的语句将完全新的列表分配给其每个元素
list_name[index] = [value0, value1, value2, …, valueM]
其中
►index 是列表中元素的索引位置。
►value0-0, value0-1, value0-2, …, valueN-M 是列表元素的值。
下面的代码片段创建了列表 grades,并将其元素分配了三个列表(及其值)。
grades = [None] * 3
grades[0] = ["B+", "A+", "A", "C-"]
grades[1] = ["B", "A", "C", "D"]
grades[2] = ["B", "B", "B-", "B+"]
第二种方法
您可以创建一个完全空的列表,并无需使用索引即可向其中添加新列表,如下面的代码片段所示,以一般形式给出。
list_name = []
list_name.append([value0-0, value0-1, value0-2, …, value0-M])
list_name.append([value1-0, value1-1, value1-2, …, value1-M])
list_name.append([value2-0, value2-1, value2-2, …, value2-M])
…
list_name.append([valueN-0, valueN-1, valueN-2, …, valueN-M])
下面的代码片段创建了列表 grades,并向其中添加了三个列表(及其值)。
grades = []
grades.append(["B+", "A+", "A", "C-"])
grades.append(["B", "A", "C", "D"])
grades.append(["B", "B", "B-", "B+"])
请注意,在此方法中,默认情况下索引编号从零开始。
第三种方法
您可以创建一个列表并直接向其中添加值,如下面的 Python 语句所示,以一般形式给出。
list_name = [
[value0-0, value0-1, value0-2, …, value0-M],
[value1-0, value1-1, value1-2, …, value1-M],
[value2-0, value2-1, value2-2, …, value2-M],
…
[valueN-0, valueN-1, valueN-2, …, valueN-M]
]
因此,可以使用以下语句创建列表 grades。
grades = [
["B+", "A+", "A", "C-"],
["B", "A", "C", "D"],
["B", "B", "B-", "B+"]
]
这也可以写成一行,如下所示:
grades = [["B+", "A+", "A", "C-"], ["B", "A", "C", "D"], ["B", "B", "B-", "B+"]]
第四种方法
最后但同样重要的是,你可以使用以下语句在 Python 中创建二维列表,以下为一般形式:
list_name = [[None] * number_of_columns for i in range(number_of_rows)]
其中 number_of_rows 和 number_of_columns 可以是任何正整数。
然后,你可以使用以下语句为列表元素赋值,以下为一般形式:
list_name[row_index][column_index] = value
其中 row_index 和 column_index 分别是列表中元素的行索引和列索引位置。
以下代码片段创建了列表 grades 并为其元素赋值。
grades = [[None] * 4 for i in range(3)]
grades[0][0] = "B+"
grades[0][1] = "A+"
grades[0][2] = "A"
grades[0][3] = "C-"
grades[1][0] = "B"
grades[1][1] = "A"
grades[1][2] = "C"
grades[1][3] = "D"
grades[2][0] = "B"
grades[2][1] = "B"
grades[2][2] = "B-"
grades[2][3] = "B+"
31.2 如何从二维列表中获取值
二维列表由行和列组成。以下示例展示了具有三行四列的二维列表。

二维列表的每个元素都可以使用一对索引唯一标识:行索引和列索引,如下所示。
list_name[row_index][column_index]
以下 Python 程序创建了一个具有三行四列的二维列表 grades,并显示了一些其元素。
grades = [
["B+", "A+", "A", "C-"],
["B", "A", "C", "D"],
["B", "B", "B-", "B+"]
]
print(grades[1][2]) #它显示:C
print(grades[2][2]) #它显示:B-
print(grades[0][0]) #它显示:B+
练习 31.2-1 创建跟踪表
为以下代码片段创建跟踪表。
a = [
[0, 0],
[0, 0],
[0, 0]
]
a[1][0] = 9
a[0][1] = 1
a[0][0] = a[0][1] + 6
x = 2
a[x][1] = a[0][0] + 4
a[x - 1][1] = a[0][1] * 3
a[x][0] = a[x - 1][1] - 3
解答
此代码片段使用一个 3×2 的列表,即一个有 3 行 2 列的列表。跟踪表如下。
| 步骤 | 语句 | 备注 | x | a |
|---|
| 1 | a = [ [0, 0],
[0, 0],
[0, 0]
] | 这将创建一个包含零值的列表 a。 | ? |
| 0 | 0 |
| 0 | 0 |
| 0 | 0 |
|
| 2 | a[1][0] = 9 | ? |
|---|
| 0 | 0 |
| 9 | 0 |
| 0 | 0 |
|
| 3 | a[0][1] = 1 | ? |
|---|
| 0 | 1 |
| 9 | 0 |
| 0 | 0 |
|
| 4 | a[0][0] = a[0][1] + 6 | ? |
|---|
| 7 | 1 |
| 9 | 0 |
| 0 | 0 |
|
| 5 | x = 2 | 2 |
|---|
| 7 | 1 |
| 9 | 0 |
| 0 | 0 |
|
| 6 | a[x][1] = a[0][0] + 4 | 2 |
|---|
| 7 | 1 |
| 9 | 0 |
| 0 | 11 |
|
| 7 | a[x − 1][1] = a[0][1] * 3 | 2 |
|---|
| 7 | 1 |
| 9 | 3 |
| 0 | 11 |
|
| 8 | a[x][0] = a[x − 1][1] - 3 | 2 |
|---|
| 7 | 1 |
| 9 | 3 |
| 0 | 11 |
|
31.3 如何遍历二维列表
由于二维列表由行和列组成,程序可以遍历行或列。
遍历行
遍历行意味着首先处理行 0,然后是行 1,接着是行 2,依此类推。下面是一个 3 × 4 列表的例子。箭头显示了在遍历行时遵循的“路径”,换句话说,它们显示了处理元素时的顺序。

3 × 4 列表是一个二维列表,有 3 行和 4 列。在 Y × X 的表示法中,第一个数字(Y)始终代表总行数,第二个数字(X)始终代表总列数。
当遍历行时,列表的元素按以下顺序处理:
►行 0 的元素按以下顺序处理
a[0][0] → a[0][1] → a[0][2] → a[0][3]
►行 1 的元素按以下顺序处理
a[1][0] → a[1][1] → a[1][2] → a[1][3]
►行 2 的元素按以下顺序处理
a[2][0] → a[2][1] → a[2][2] → a[2][3]
第一种方法
使用 Python 语句,让我们尝试通过遍历行来处理 3 × 4 列表(3 行 × 4 列)的所有元素。
i = 0 # 变量 i 指向行 0。
for j in range(4): # 这个循环控制结构处理行 0 的所有元素
process a[i][j]
i = 1 # 变量 i 指向行 1。
for j in range(4): # 这个循环控制结构处理行 1 的所有元素
process a[i][j]
i = 2 # 变量 i 指向行 2。
for j in range(4): # 这个循环控制结构处理行 2 的所有元素
process a[i][j]
当然,可以使用下面的嵌套循环控制结构达到相同的结果。
for i in range(3):
for j in range(4):
process a[i][j]
让我们看看一些例子。以下代码片段允许用户将 10 × 10 = 100 个值输入到列表 b 中。
for i in range(10):
for j in range(10):
b[i][j] = input()
以下代码片段将列表 b 的所有值减一。
for i in range(10):
for j in range(10):
b[i][j] -= 1 # 等价于:b[i][j] = b[i][j] − 1
以下代码片段显示了列表 b 的所有元素。
for i in range(10):
for j in range(10):
print(b[i][j], end = "\t")
print()
print()语句用于在行之间“显示”换行符。
第二种方法
也有另一种非常简单但不如前一种灵活的方法。在某些情况下,它不能使用,您将在下面看到。以下是一个通用形式的代码片段
for row in list_name:
for element in row:
process element
其中 process 是每次迭代处理列表中一个元素的任何 Python 语句或语句块。
以下 Python 程序,在每次迭代中显示列表 b 的所有元素。
for row in b:
for element in row:
print(element, end = "\t")
print()
请记住,这种方法不能用来改变列表中元素的价值。例如,如果你想将列表 numbers 中所有元素的价值加倍,你不能这样做:
numbers = [
[5, 10, 3, 2],
[2, 4, 1, 6]
]
for x in numbers:
for number in x:
number = number * 2
遍历列
遍历列意味着首先处理列 0,然后是列 1,接着是列 2,依此类推。以下是一个 3 × 4 列表的例子。箭头显示了处理元素的顺序。

当遍历列时,列表的元素按以下顺序处理:
►第 0 列的元素按以下顺序处理
a[0][0] → a[1][0] → a[2][0]
►第 1 列的元素按以下顺序处理
a[0][1] → a[1][1] → a[2][1]
►第 2 列的元素按以下顺序处理
a[0][2] → a[1][2] → a[2][2]
►第 3 列的元素按以下顺序处理
a[0][3] → a[1][3] → a[2][3]
使用 Python 语句,让我们尝试通过遍历列来处理一个 3 × 4 的列表(3 行 × 4 列)的所有元素。
j = 0 # 变量 j 指代第 0 列。
for i in range(3): # 这个循环控制结构处理第 0 列的所有元素
process a[i][j]
j = 1 # 变量 j 指代第 1 列。
for i in range(3): # 这个循环控制结构处理第 1 列的所有元素
process a[i][j]
j = 2 # 变量 j 指代第 2 列。
for i in range(3): # 这个循环控制结构处理第 2 列的所有元素
process a[i][j]
j = 3 # 变量 j 指代第 3 列。
for i in range(3): # 这个循环控制结构处理第 3 列的所有元素
process a[i][j]
当然,可以使用嵌套循环控制结构达到相同的结果,如下所示。
for j in range(4):
for i in range(3):
process a[i][j]
如您所见,这段代码片段与遍历行的代码片段只有一个不同点:两个 for 循环的位置互换了。但请注意。在 process a[i][j] 的语句中,永远不要互换索引变量 i 和 j 的位置。以下是一个例子。它试图遍历一个 3 × 4 的列表(3 行 × 4 列),但它不满足确定性的性质。你能找出原因吗?
for j in range(4):
for i in range(3):
处理 process a[j][i]
问题出现在变量 j 等于 3 时。语句 process a[j][i] 尝试处理第 3 行的元素(这是第四行),当然,这是不存在的!还是不明白?别担心!在 3 × 4 的列表中根本不存在第 3 行索引!由于行索引编号从 0 开始,实际上只有行 0、1 和 2 存在!
31.4 如何将用户输入的值添加到二维列表中
正如在单维列表中一样,您可以直接将键盘输入的值直接赋给特定的列表元素,而不仅仅是读取一个值并将其赋给一个变量。以下代码片段创建了一个二维列表 names,提示用户输入六个值,并将这些值赋给列表的元素。
names = [[None] * 2 for i in range(3)]
names[0][0] = input("为第 0 行第 0 列输入名称: ")
names[0][1] = input("为第 0 行第 1 列输入名称: ")
names[1][0] = input("为第 1 行第 0 列输入名称: ")
names[1][1] = input("为第 1 行第 1 列输入名称: ")
names[2][0] = input("为第 2 行第 0 列输入名称: ")
names[2][1] = input("为第 2 行第 1 列输入名称: ")
使用嵌套循环,此代码片段可以等效地写成
ROWS = 3
COLUMNS = 2
names = [[None] * COLUMNS for i in range(ROWS)]
for i in range(ROWS):
for j in range(COLUMNS):
names[i][j] = input("为第 " + str(i) + " 行第 " + str(j) + " 列输入名称: ")
当然,您也可以使用 append() 方法完成同样的操作,如下面的代码片段所示。
ROWS = 3
COLUMNS = 2
names = [] # 创建一个完全空的列表
for i in range(ROWS):
names.append([]) # 在列表 names 中添加一个完全空的列表
for j in range(COLUMNS):
name = input("为第 " + str(i) + " 行第 " + str(j) + " 列输入名称: ")
names[i].append(name)
练习 31.4-1 仅显示实数
编写一个 Python 程序,提示用户在一个 5 × 7 的列表中输入数值,然后显示包含实数的元素索引。
Solution
遍历行是最常用的方法,所以让我们使用它。解决方案如下。
file_31.4-1
ROWS = 5
COLUMNS = 7
a = [[None] * COLUMNS for i in range(ROWS)]
for i in range(ROWS):
for j in range(COLUMNS):
a[i][j] = float(input("为元素 " + str(i) + ", " + str(j) + " 输入值: "))
for i in range(ROWS):
for j in range(COLUMNS):
if a[i][j] != int(a[i][j]): # 检查是否为实数(浮点数)
print("在位置找到实数:", i , ",", j)
练习 31.4-2 仅显示奇数列
编写一个 Python 程序,提示用户在一个 5 × 7 的列表中输入数值,然后显示具有奇数索引的列的元素(即列索引 1、3 和 5)。
Solution
下面的 Python 程序展示了如何使用 append() 方法将用户输入的值添加到列表 b 中。
file_31.4-2
ROWS = 5
COLUMNS = 7
b = []
for i in range(ROWS):
b.append([])
for j in range(COLUMNS):
b[i].append(float(input("Enter a value for element " + str(i) + ", " + str(j) + ": ")))
遍历列
for j in range(1, COLUMNS, 2): #从 1 开始,每次增加 2
for i in range(ROWS):
print(b[i][j])
这本书尽可能地使用变量 i 作为行索引,变量 j 作为列索引。当然,你也可以使用其他变量名,例如 row、r 作为行索引,或 column、c 作为列索引,但 i 和 j 是大多数程序员广泛使用的变量。使用了一段时间之后,你的大脑就会将 i 与行联系起来,将 j 与列联系起来。因此,每个使用这些变量名作为二维列表索引的算法或程序都将更容易理解。
31.5 变量 i 和 j 的故事是什么?
许多程序员认为变量 i 的名字代表“索引”,而 j 被使用仅仅是因为它跟在 i 的后面。其他人认为 i 的名字代表“整数”。可能真相就在中间某个地方。
在计算机出现之前,数学家就已经使用 i、j 和 k 来表示整数。后来,在 FORTRAN 中,作为第一种高级计算机语言之一,变量 i、j、k、l、m 和 n 默认为整数。因此,最初的程序员在他们的程序中开始使用变量 i 和 j,这成为了大多数计算机语言中的惯例。
31.6 方阵
在数学中,具有相同行数和列数的矩阵称为方阵。以下是一些方阵的示例。


练习 31.6-1 查找主对角线上元素的总和
编写一个 Python 程序,允许用户输入数值到一个 10 × 10 的列表中,然后计算其主对角线上元素的总和。
解决方案
在数学中,一个方阵的主对角线是从左上角到右下角运行的那些元素的集合。以下是一些带有主对角线以深色背景突出显示的方阵示例。


注意,主对角线上的元素其行索引等于其列索引。
你可以使用两种不同的方法来计算主对角线上元素的总和。让我们来研究这两种方法。
第一种方法 – 遍历所有元素
在这种方法中,程序遍历行并检查行索引是否等于列索引。对于表示为 N × N 的方阵(在这种情况下,列表),行数和列数相等,因此你可以定义一个常数 N。解决方案如下。
file_31.6-1a
N = 10
a = [[None] * N for i in range(N)]
for i in range(N):
for j in range(N):
a[i][j] = float(input())
计算总和
total = 0
for i in range(N):
for j in range(N):
if i == j:
total += a[i][j] # 这等价于:total = total + a[i][j]
print("Sum =", total)
请注意,程序遍历行并检查行索引是否等于列索引。或者,可以通过遍历列得到相同的结果。
在这种方法中,负责计算总和的嵌套循环结构执行了 10 × 10 = 100 次迭代。
第二种方法 – 直接遍历主对角线
在这种方法中,单个循环控制结构直接遍历主对角线。解答如下。
file_31.6-1b
N = 10
a = []
for i in range(N):
a.append([])
for j in range(N):
a[i].append(float(input()))
计算总和
total = 0
for k in range(N):
total += a[k][k]
print("Sum =", total)
这种方法比第一种方法更高效,因为负责计算总和的 for 循环只执行了 10 次迭代。
练习 31.6-2 计算反对角线上元素的总和
编写一个 Python 程序,允许用户在一个 5 × 5 列表中输入数值,然后计算其反对角线上元素的总和。
解答
在数学中,方阵的反对角线是从列表的右上角到左下角的元素集合。接下来,你可以找到一个 5 × 5 方阵的例子,其反对角线通过深色背景突出显示。
.
N × N 列表中任何反对角线元素的索引满足以下方程:
i + j = N - 1
其中变量 i 和 j 分别对应行和列索引。
如果解出 j,方程变为:
j = N - i - 1
使用这个公式,你可以计算出反对角线上任何元素的索引;也就是说,对于变量 i 的任何值,你可以找到变量 j 的对应值。例如,在之前的 5 × 5 方阵列表中,N 等于 5,当 i 为 3 时,变量 j 的值为:
j = N - i - 1 ⟺ j = 5 − 3 − 1 ⟺ j = 1
Using all this knowledge, let's now write the corresponding Python program.
file_31.6-2
N = 5
a = [[None] * N for i in range(N)]
for i in range(N):
for j in range(N):
a[i][j] = float(input())
计算总和
total = 0
for i in range(N):
j = N - i - 1 # 等价于:
total += a[i][j] # total += a[i][N - i - 1]
print("Sum =", total)
请注意,负责寻找反对角线上元素总和的 for 循环直接遍历反对角线。
练习 31.6-3 填充列表
编写一个 Python 程序,创建并显示以下列表。

解答
正如你所见,主对角线上有-1 的值。你已经知道,方阵主对角线上的元素的一个共同特点是它们的行索引等于它们的列索引。现在,你还需要找到包含值 10 的所有元素之间的一个共同特点,以及包含值 20 的所有元素之间的另一个这样的共同特点。实际上,确实存在这样的特点!任何包含值 10 的元素的行索引在所有情况下都大于其对应的列索引,同样,任何包含值 20 的元素的行索引在所有情况下都小于其对应的列索引。
因此,Python 程序如下所示。
file_31.6-3
N = 5
a = [[None] * N for i in range(N)]
for i in range(N):
for j in range(N):
if i == j:
a[i][j] = -1
elif i > j:
a[i][j] = 10
else:
a[i][j] = 20
for i in range(N):
for j in range(N):
print(a[i][j], end = "\t")
print()
31.7 复习问题:对/错
对以下每个陈述选择对或错。
1)二维列表的所有元素必须包含不同的值。
2)为了引用二维列表中的元素,你需要两个索引。
3)二维列表的两个索引必须是两个变量,或者两个常量值。
4)一个 5×6 的列表是一个具有五列和六行的二维列表。
5)要引用列表 b 中第二行第三列的元素,你会写 b[2][3]。
6)遍历行意味着二维列表的第一行首先被处理,然后是第二行,依此类推。
7)你不能使用除了 i 和 j 之外的变量来遍历二维列表。
8)以下 Python 语句创建了一个二维列表。
names = [[None] * 2 for i in range(6)]
9)以下代码片段创建了一个包含四个元素的二维列表,并给它们赋值。
names = [[None] * 2 for i in range(2)]
names[0][0] = "John"
names[0][1] = "George"
names[1][0] = "Sally"
names[1][1] = "Angelina"
10)以下代码片段将值 10 赋给索引为 0 的行中的元素。
values[0][0] = 7
values[0][values[0][0]] = 10
11)以下语句将“Sally”这个名字添加到索引为 1 的行中的元素。
names = [["John", "George"], ["Sally", "Angelina"]]
12)以下代码片段在屏幕上显示“Sally”这个名字。
names = [[None] * 2 for i in range(2)]
k = 0
names[0][k] = "John"
k += 1
names[0][k] = "George"
names[1][k] = "Sally"
k -= 1
names[1][k] = "Angelina"
print(names[1][1])
13)以下代码片段满足确定性的特性。
grades = [["B+", "A+"], ["A", "C-"]]
print(grades[2][2])
14)以下代码片段满足确定性的特性。
values = [[1, 0], [2, 0]]
print(values[values[0][0]][values[0][1]])
15)以下代码片段在屏幕上显示值 2。
values = [[0, 1], [2, 0]]
print(values[values[0][1]][values[0][0]])
16)以下代码片段显示了一个 3 × 4 列表的所有元素。
for k in range(12):
i, j = divmod(k, 4)
print(names[i][j])
17)以下代码片段允许用户将 100 个值输入到列表 b 中。
for i in range(10):
for j in range(10):
b[i][j] = input()
18)如果列表 b 包含 10 × 20 个元素,以下代码片段将所有元素值加倍。
for i in range(9, -1, -1):
for j in range(19, -1, -1):
b[i][j] *= 2
19)如果列表 b 包含 10 × 20 个元素,以下代码片段显示其中的一些。
for i in range(0, 10, 2):
for j in range(20):
print(b[i][j])
for i in range(1, 10, 2):
for j in range(20):
print(b[i][j])
20)以下代码片段仅显示具有偶数索引的列。
for j in range(0, 12, 2):
for i in range(10):
print(a[i][j])
21)一个 5 × 5 的列表是一个正方形列表。
22)在 N × N 列表的主对角线上,所有元素的行索引等于它们的列索引。
23)在数学中,一个正方形矩阵的反对角线是从列表的左上角到右下角的所有元素的集合。
24)一个 N × N 列表反对角线上的任何元素都满足方程 i + j = N − 1,其中变量 i 和 j 分别对应行和列索引。
25)以下代码片段计算了一个 N × N 列表主对角线上元素的总和。
total = 0
for k in range(N):
total += a[k][k]
26)以下代码片段显示了一个 N × N 列表反对角线上的所有元素。
for i in range(N - 1, -1, -1):
print(a[i][N - i - 1])
27)任何位于 N × N 列表主对角线下方的元素的列索引总是大于其对应的行索引。
31.8 复习题:多项选择题
选择以下每个语句的正确答案。
1)以下语句
lastNames = [None] * 5 for i in range(4)
a)包含逻辑错误。
b)包含语法错误。
c)是一个正确的语句。
d)以上皆非
2)以下代码片段
values = [[1, 0] [2, 0]]
print(values[values[0][0], values[0][1]])
a)包含逻辑错误。
b)包含语法错误。
c)以上皆非
3)以下代码片段
x = int(input())
y = int(input())
names[x][y] = 10
a)不满足有限性的属性。
b)不满足有效性的属性。
c)不满足确定性的属性。
d)以上皆非
4)如果变量 x 包含值 4,以下语句
names[x + 1][x] = 5
a)将值 5 赋给行索引为 5 和列索引为 4 的元素。
b)将值 5 赋给行索引为 4 和列索引为 5 的元素。
c)将值 5 赋给行索引为 5 和列索引为 5 的元素。
d)以上皆非
5)以下语句
names = [[3, 5, 2]]
a)将值 5 赋给行索引为 0 和列索引为 1 的元素。
b)将值 3 赋给行索引为 0 和列索引为 0 的元素。
c)将值 2 赋给行索引为 0,列索引为 2 的元素。
d)所有上述选项
e)以上皆非
6)以下语句
values = [[None] * 2 ]
a)创建一个 1 × 2 列表。
b)创建一个 2 × 1 列表。
c)创建一个一维列表。
d)以上皆非
7)你可以使用两个嵌套循环控制结构遍历二维列表,
a)变量 i 和 j 作为计数器。
b)变量 k 和 l 作为计数器。
c)变量 m 和 n 作为计数器。
d)使用任何变量作为计数器。
8)以下代码片段
names = [["John", "Sally"], ["George", "Maria"]]
for j in range(2):
for i in range(1, -1, -1):
print(names[i][j])
a)按降序显示所有名称。
b)按降序显示一些名称。
c)按升序显示所有名称。
d)按升序显示一些名称。
e)以上皆非
9)如果列表 b 包含 30 × 40 个元素,以下代码片段
for i in range(30, 0, -1):
for j in range(40, 0, -1):
b[i][j] *= 3
a)将一些元素值乘以 3。
b)将所有元素值乘以 3。
c)不满足确定性的性质。
d)以上皆非
10)如果列表 b 包含 30 × 40 个元素,以下代码片段
total = 0
for i in range(29, -1, -1):
for j in range(39, -1, -1):
total += b[i][j]
average = total / 120
a)计算所有元素的总和。
b)计算所有元素的平均值。
c)所有上述选项
11)以下两个代码片段计算 N × N 列表主对角线上元素的总和,
total = 0
for i in range(N):
for j in range(N):
if i == j:
total += a[i][j]
total = 0
for k in range(N):
total += a[k][k]
a)但第一个更高效。
b)但第二个更高效。
c)以上皆非;两个代码片段执行效果相同
31.9 复习练习
Complete the following exercises.
1)为以下代码片段创建跟踪表。
a = [[None] * 3 for i in range(2)]
a[0][2] = 1
x = 0
a[0][x] = 9
a[0][x + a[0][2]] = 4
a[a[0][2]][2] = 19
a[a[0][2]][x + 1] = 13
a[a[0][2]][x] = 15
2)为以下代码片段创建跟踪表。
a = [[None] * 3 for i in range(2)]
for i in range(2):
for j in range(3):
a[i][j] = (i + 1) * 5 + j
3)为以下代码片段创建跟踪表。
a = [[None] * 3 for i in range(3)]
for j in range(3):
for i in range(3):
a[i][j] = (i + 1) * 2 + j * 4
4)尝试不使用跟踪表,确定在以下代码片段执行后列表将包含的值。为此进行三次不同的执行。相应的输入值是:(i) 5,(ii) 9,和 (iii) 3。
a = [[None] * 3 for i in range(2)]
x = int(input())
for i in range(2):
for j in range(3):
a[i][j] = (x + i) * j
5)尝试不使用跟踪表,确定在以下代码片段执行后列表将包含的值。为此进行三次不同的执行。相应的输入值是:(i) 13,(ii) 10,和 (iii) 8。
a = [[None] * 3 for i in range(2)]
x = int(input())
for i in range(2):
for j in range(3):
if j < x % 4:
a[i][j] = (x + i) * j
else:
a[i][j] = (x + j) * i + 3
6)在不使用跟踪表的情况下,尝试确定以下代码片段执行后列表将包含的值。
a = [[18, 10, 35], [32, 12, 19]]
for j in range(3):
for i in range(2):
if a[i][j] < 13:
a[i][j] /= 2
elif a[i][j] < 20:
a[i][j] += 1
else:
a[i][j] -= 4
7)在不使用跟踪表的情况下,尝试确定以下代码片段执行后列表将包含的值。
a = [[11, 10], [15, 19], [22, 15]]
for j in range(2):
for i in range(3):
if i == 2:
a[i][j] += a[i - 1][j]
else:
a[i][j] += a[i + 1][j]
8)假设列表 a 包含以下值。
.
执行以下每个代码片段后,屏幕上显示什么?
i)
for i in range(3):
for j in range(3):
print(a[i][j], end = " ")
ii)
for i in range(2, -1, -1):
for j in range(3):
print(a[i][j], end = " ")
iii)
for i in range(3):
for j in range(2, -1, -1):
print(a[i][j], end = " ")
iv)
for i in range(2, -1, -1):
for j in range(2, -1, -1):
print(a[i][j], end = " ")
v)
for j in range(3):
for i in range(3):
print(a[i][j], end = " ")
vi)
for j in range(3):
for i in range(2, -1, -1):
print(a[i][j], end = " ")
vii)
for j in range(2, -1, -1):
for i in range(3):
print(a[i][j], end = " ")
viii)
for j in range(2, -1, -1):
for i in range(2, -1, -1):
print(a[i][j], end = " ")
9)编写一个 Python 程序,允许用户在一个 10 × 15 列表中输入整数,然后显示包含奇数的元素的索引。
10)编写一个 Python 程序,允许用户在一个 10 × 6 列表中输入数值,然后显示索引为偶数的列的元素(即列索引 0、2 和 4)。
11)编写一个 Python 程序,允许用户在一个 12 × 8 列表中输入数值,然后计算并显示具有偶数列索引和奇数行索引的元素的和。
12)编写一个 Python 程序,允许用户在一个 8 × 8 正方形列表中输入数值,然后计算主对角线和副对角线上元素的平均值。尝试在同一个循环控制结构中计算这两个平均值。
13)编写一个 Python 程序,创建并显示以下列表。

14)编写一个 Python 程序,创建并显示以下列表。

15)编写一个 Python 程序,允许用户在一个 5 × 4 列表中输入数值,然后显示包含整数的元素行和列索引。
16)编写一个 Python 程序,允许用户在一个 10 × 4 列表中输入数值,然后计算并显示负元素的总数。
17)编写一个 Python 程序,允许用户在一个 3 × 4 列表中输入单词,然后以空格字符显示它们。
- 编写一个 Python 程序,允许用户在一个 20 × 14 的列表中输入单词,然后显示那些少于五个字符的单词。
提示:使用 len()函数。
- 编写一个 Python 程序,允许用户在一个 20 × 14 的列表中输入单词,并显示那些少于 5 个字符的单词,然后是少于 10 个字符的单词,最后是少于 20 个字符的单词。
提示:尝试使用三个嵌套的循环来显示这些单词。
第三十二章
数据结构技巧与窍门
32.1 简介
由于列表是使用与之前章节中学习的相同序列、决策和循环控制结构处理的,因此在这里不需要重复所有这些信息。然而,在本章中,你将发现如何单独处理二维列表的每一行或每一列,如何解决需要使用多个列表的问题,如何从一个一维列表创建二维列表(反之亦然),以及 Python 支持的一些有用的内置列表函数和方法。
32.2 单独处理每一行
单独处理每一行意味着每一行都是单独处理的,每一行的结果(可以是总和、平均值等)可以单独用于进一步处理。
假设你有一个 4×5 的列表。
.
让我们尝试找到每一行的总和。以下两种方法都会遍历行。
第一种方法 – 创建辅助列表
在这种方法中,程序单独处理每一行并创建一个辅助列表,其中每个元素存储一行之和。这种方法提供了很大的灵活性,因为您可以在程序中稍后使用这个新列表进行进一步处理。辅助列表 total 显示在右侧。

现在,让我们编写相应的代码片段。为了更易于理解这个过程,我们使用“从内到外”的方法。以下代码片段计算第一行的总和(行索引为 0),并将结果存储在辅助列表 total 的位置 0 处。假设变量 i 包含值 0。
s = 0
for j in range(COLUMNS):
s += b[i][j]
total[i] = s
这个代码片段可以等价地写成
total[i] = 0
for j in range(COLUMNS):
total[i] += b[i][j]
现在,将此代码片段嵌套在一个遍历所有行的 for 循环中,结果如下。
total = [None] * ROWS
for i in range(ROWS):
total[i] = 0
for j in range(COLUMNS):
total[i] += b[i][j]
然而,同样可以使用 Python 的神奇力量来实现相同的结果!代码片段如下。
total = []
for row in b:
total.append(fsum(row))
第二种方法 – 只找到它并处理它。
这种方法不使用任何辅助列表;它只是计算并直接处理总和。代码片段如下。
for i in range(ROWS):
total = 0
for j in range(COLUMNS):
total += b[i][j]
过程总和
过程总和是什么意思?这取决于给定的问题。它可能只是显示总和,也可能计算每一行的平均值并显示,或者它可能使用总和来计算更复杂的数学表达式。
例如,以下示例计算并显示列表 b 每一行的平均值。
for i in range(ROWS):
total = 0
for j in range(COLUMNS):
total += b[i][j]
average = total / COLUMNS
print(average)
同样的结果,可以使用 Python 的魔法力实现!以下是一个代码片段
for row in b:
total = fsum(row)
print(total / COLUMNS)
练习 32.2-1 查找平均值
有 20 名学生,每个学生都完成了 10 节课的评分。编写一个 Python 程序,提示用户输入每个学生的所有课程成绩,然后计算并显示每个学生的所有大于 89 的平均值。
解决方案
由于你已经学会了两种逐行处理的方法,让我们同时使用它们。
第一种方法 – 创建辅助列表
在这种方法中,程序逐行处理,并在辅助列表中创建每个元素,存储一行平均值。以下显示两个所需的列表。

在创建列表 average 之后,程序可以找到并显示所有大于 89 的平均值。以下是一个 Python 程序。
file_32.2-1a
STUDENTS = 20
LESSONS = 10
grades = [[None] * LESSONS for i in range(STUDENTS)]
for i in range(STUDENTS):
print("对于学生编号", i + 1, "...")
for j in range(LESSONS):
grades[i][j] = int(input("输入第 " + str(j + 1) + " 节课的成绩: "))
创建列表 average。遍历行
average = [None] * STUDENTS
for i in range(STUDENTS):
average[i] = 0
for j in range(LESSONS):
average[i] += grades[i][j]
average[i] /= LESSONS
显示所有大于 89 的平均值
for i in range(STUDENTS):
if average[i] > 89:
print(average[i])
使用 Python 的魔法力,辅助列表 average 也可以像下面这样创建。
file_32.2-1b
from math import fsum
STUDENTS = 20
LESSONS = 10
grades = [[None] * LESSONS for i in range(STUDENTS)]
for i in range(STUDENTS):
print("对于学生编号", i + 1, "...")
for j in range(LESSONS):
grades[i][j] = int(input("输入第 " + str(j + 1) + " 节课的成绩: "))
创建列表 average。遍历行
average = []
for row in grades:
average.append(fsum(row) / LESSONS)
显示所有大于 89 的平均值
for i in range(STUDENTS):
if average[i] > 89:
print(average[i])
第二种方法 – 只找到并显示它!
这种方法不使用任何辅助列表;它只是计算并直接显示所有大于 89 的平均值。以下是一个 Python 程序。
file_32.2-1c
STUDENTS = 20
LESSONS = 10
grades = [[None] * LESSONS for i in range(STUDENTS)]
for i in range(STUDENTS):
print("对于学生编号", i + 1, "...")
for j in range(LESSONS):
grades[i][j] = int(input("输入第 " + str(j + 1) + " 节课的成绩: "))
计算每行的平均值并直接显示大于 89 的那些
for i in range(STUDENTS):
average = 0
for j in range(LESSONS):
average += grades[i][j]
average /= LESSONS
if average > 89:
print(average)
再次,使用 Python 的魔法力量和不使用任何辅助列表,Python 程序如下。
file_32.2-1d
from math import fsum
STUDENTS = 20
LESSONS = 10
grades = []
for i in range(STUDENTS):
grades.append([])
print("对于学生编号", i + 1, "...")
for j in range(LESSONS):
grades[i].append(int(input("输入第" + str(j + 1) + "节课的成绩: ")))
使用 Python 的魔法力量计算每行的平均值!
for row in grades:
average = fsum(row) / LESSONS
if average > 89:
print(average)
32.3 逐列处理
逐列处理意味着每列都是单独处理的,每列的结果(可以是总和、平均值等)可以单独用于进一步处理。假设你有一个以下 4 × 5 的列表。

如前所述,让我们尝试逐列求和。再次,你可以使用两种方法。这两种方法都是遍历列。
第一种方法 - 创建辅助列表
在这种方法中,程序逐列处理并创建一个辅助列表,其中每个元素存储一列的总和。这种方法提供了很大的灵活性,因为你可以稍后在程序中使用这个新列表进行进一步处理。辅助列表 total 显示在底部。

现在,让我们编写相应的代码片段。为了更易于理解过程,再次使用“从内到外”的方法。以下代码片段计算第一列(列索引 0)的总和,并将结果存储在辅助列表 total 的位置 0。假设变量 j 包含值 0。
s = 0
for i in range(ROWS):
s += b[i][j]
total[j] = s
这个程序可以等价地写成
total[j] = 0
for i in range(ROWS):
total[j] += b[i][j]
现在,将此代码片段嵌套在一个遍历所有列的 for 循环中,结果如下。
total = [None] * COLUMNS
for j in range(COLUMNS):
total[j] = 0
for i in range(ROWS):
total[j] += b[i][j]
第二种方法 - 只找到它并处理它。
这种方法不使用任何辅助列表;它只是计算并直接处理总和。代码片段如下。
for j in range(COLUMNS):
total = 0
for i in range(ROWS):
total += b[i][j]
处理总和
因此,以下代码片段计算并显示每列的平均值。
for j in range(COLUMNS):
total = 0
for i in range(ROWS):
total += b[i][j]
print(total / ROWS)
练习 32.3-1 查找平均值
有 10 名学生,他们每人已经完成了 5 节课的成绩。编写一个 Python 程序,提示用户输入每个学生的所有课程成绩,然后计算并显示每节课的平均值,其中平均值大于 89。
解答
既然你已经学会了两种逐列处理的方法,让我们都使用它们。
第一种方法 – 创建辅助列表
在这种方法中,程序逐列处理每个列,并创建一个辅助列表,其中每个元素存储一个列的平均值。接下来将展示两个所需的列表。

在创建列表 average 之后,程序可以找到并显示所有大于 89 的平均值。Python 程序如下。
file_32.3-1a
STUDENTS = 10
LESSONS = 5
grades = [[None] * LESSONS for i in range(STUDENTS)]
for i in range(STUDENTS):
print("学生编号", i + 1, "...")
for j in range(LESSONS):
grades[i][j] = int(input("输入第" + str(j + 1) + "课的分数: "))
创建列表 average。遍历列
average = [None] * LESSONS
for j in range(LESSONS):
average[j] = 0
for i in range(STUDENTS):
average[j] += grades[i][j]
average[j] /= STUDENTS
显示所有大于 89 的平均值
for j in range(LESSONS):
if average[j] > 89:
print(average[j])
第二种方法 – 只找到它并显示它!
这种方法不使用辅助列表;它只是计算并直接显示所有大于 89 的平均值。Python 程序如下。
file_32.3-1b
STUDENTS = 10
LESSONS = 5
grades = [[None] * LESSONS for i in range(STUDENTS)]
for i in range(STUDENTS):
print("学生编号", i + 1, "...")
for j in range(LESSONS):
grades[i][j] = int(input("输入第" + str(j + 1) + "课的分数: "))
计算每列的平均值
直接显示分数大于 89 的学生
for j in range(LESSONS):
average = 0
for i in range(STUDENTS):
average += grades[i][j]
average /= STUDENTS
if average > 89:
print(average)
32.4 在程序中使用多个数据结构
到目前为止,每个示例或练习都只使用了一个列表或一个字典。但如果一个问题需要你使用两个列表,或者一个列表和一个字典,或者一个列表和两个字典呢?接下来你将找到一些练习,展示如何将各种数据结构组合起来以应对各种独特的挑战。
练习 32.4-1 使用三个一维列表
有 20 名学生,每个学生都为两节课获得了分数。编写一个 Python 程序,提示用户输入每个学生的姓名和两节课的分数。然后程序必须找到并显示所有两节课分数都大于 89 的学生姓名。
解决方案
下面是包含一些典型值的所需列表。

如您所见,列表 names 中的元素与列表 gradesLesson1 和 gradesLesson2 中的元素之间存在一一对应关系。前二十名学生是乔治,他在两门课程中分别获得了 95 和 92 分。姓名“乔治”存储在列表 names 的索引 0 处,在列表 gradesLesson1 和 gradesLesson2 中,他的两门课程的分数存储在完全相同的索引位置。下一个学生(安娜)及其分数存储在列表 names、gradesLesson1 和 gradesLesson2 的索引 1 处,依此类推。
以下是一个 Python 程序。
file_32.4-1
STUDENTS = 20
names = [None] * STUDENTS
gradesLesson1 = [None] * STUDENTS
gradesLesson2 = [None] * STUDENTS
for i in range(STUDENTS):
names[i] = input("输入学生姓名 No" + str(i + 1) + ": ")
gradesLesson1[i] = int(input("输入课程 1 的分数: "))
gradesLesson2[i] = int(input("输入课程 2 的分数: "))
显示那些两门课程分数都超过 89 的学生的姓名
for i in range(STUDENTS):
if gradesLesson1[i] > 89 and gradesLesson2[i] > 89:
print(names[i])
练习 32.4-2 使用一维列表和二维列表
有 10 名学生,他们每人已经完成了五门课程的考试。编写一个 Python 程序,提示用户输入每个学生的姓名和所有课程的分数,然后计算并显示那些有两门以上课程分数超过 89 的学生的姓名。
解答
在这个练习中,您可以像上一个练习那样做。例如,您可以使用一个一维列表来存储学生的姓名,以及五个一维列表来存储每个学生在每门课程的分数。这不太方便,但可以工作。显然,当分数超过两个时,这不是最合适的方法。
这里最好的方法是使用一个一维列表来存储学生的姓名,以及一个二维列表来存储每个学生在每门课程的分数。
实际上存在两种方法。使用哪一种取决于您!如果您决定在二维列表中,行应指学生,列应指课程,那么您可以使用下面讨论的第一种方法。如果您决定行应指课程,列应指学生,那么您可以使用下面的第二种方法。
第一种方法 – 行为学生,列为课程
在这种方法中,二维列表必须有 10 行,每行代表一个学生,5 列,每列代表一门课程。所有其他列表都可以根据这个二维列表如下排列。

程序将创建辅助列表 count,并存储每个学生分数超过 89 的分数数量。
现在,让我们看看如何读取值并将它们存储在 names 和 grades 列表中。一个简单的解决方案是使用一个 for 循环读取姓名,然后使用嵌套的 for 循环读取成绩。然而,对于用户来说,首先输入所有姓名然后所有成绩可能不太实用。一个更用户友好的方法是提示用户输入一个学生姓名然后所有他们的成绩,然后继续下一个学生姓名及其对应的成绩,依此类推。解决方案如下。
file_32.4-2a
STUDENTS = 10
LESSONS = 5
一次性读取姓名和成绩。遍历 grades 列表中的行
names = [None] * STUDENTS
grades = [[None] * LESSONS for i in range(STUDENTS)]
for i in range(STUDENTS):
names[i] = input("Enter name for student No." + str(i + 1) + ": ")
for j in range(LESSONS):
grades[i][j] = int(input("Enter grade No." + str(j + 1) + " for " + names[i] + ": "))
创建 count 列表。遍历行
count = [None] * STUDENTS
for i in range(STUDENTS):
count[i] = 0
for j in range(LESSONS):
if grades[i][j] > 89:
count[i] += 1
显示成绩超过 89 分的学生姓名
for i in range(STUDENTS):
if count[i] > 1:
print(names[i])
第二种方法 - 行对应课程,列对应学生
在这种方法中,二维列表必须包含 5 行,每行对应一个课程,10 列,每列对应一个学生。所有其他列表都可以相对于这个二维列表放置,如下所示。

![img/notice.jpg]辅助列表 count 将由程序创建,并将存储每个学生成绩超过 89 分的数量。
显然,为了创建 count 列表,程序将遍历列。解决方案如下。
file_32.4-2b
STUDENTS = 10
LESSONS = 5
一次性读取姓名和成绩。遍历 grades 列表中的列
names = [None] * STUDENTS
grades = [[None] * STUDENTS for i in range(LESSONS)]
for j in range(STUDENTS):
names[j] = input("Enter name for student No." + str(j + 1) + ": ")
for i in range(LESSONS):
grades[i][j] = int(input("Enter grade No." + str(i + 1) + " for " + names[j] + ": "))
创建 count 列表。遍历列
count = [None] * STUDENTS
for j in range(STUDENTS):
count[j] = 0
for i in range(LESSONS):
if grades[i][j] > 89:
count[j] += 1
显示成绩超过 89 分的学生姓名
for j in range(STUDENTS):
if count[j] > 1:
print(names[j])
练习 32.4-3 使用列表和字典
有 30 名学生,他们每个人都已经收到了他们的测试成绩。编写一个 Python 程序,提示用户输入每个学生的成绩(作为字母)。然后根据以下表格显示每个学生的成绩百分比。
| Grade | Percentage |
|---|---|
| A | 90 – 100 |
| B | 80 – 89 |
| C | 70 – 79 |
| D | 60 – 69 |
| E / F | 0 – 59 |
解决方案
字典可以用来存储给定的表格。解决方案简单明了,无需进一步解释。
file_32.4-3
STUDENTS = 30
gradesTable = {"A": "90-100", "B": "80-89", "C": "70-79",
"D": "60-69", "E": "0-59", "F": "0-59"}
names = [None] * STUDENTS
grades = [None] * STUDENTS
for i in range(STUDENTS):
names[i] = input("Enter student name No" + str(i + 1) + ": ")
grades[i] = input("Enter their grade: ")
for i in range(STUDENTS):
grade = grades[i]
gradeAsPercentage = gradesTable[grade]
print(names[i], gradeAsPercentage)
现在,如果您完全理解了最后一个 for 循环的工作原理,那么请查看下面的代码片段。它与最后一个 for 循环等效,但效率更高,因为它使用了更少的变量!
for i in range(STUDENTS):
print(names[i], gradesTable[grades[i]])
32.5 从二维列表创建一维列表
为了更轻松地理解如何从二维列表创建一维列表,让我们用一个例子来说明。
编写一个 Python 程序,从现有的 3 × 4 二维列表(如下所示)创建一个包含 12 个元素的一维列表,如下所示:二维列表的第一列的元素必须放置在一维列表的前三个位置,第二列的元素必须放置在接下来的三个位置,依此类推。
以下是二维 3 × 4 列表和新的一个维列表的展示。

下面的 Python 程序创建了一个新的一个维列表,通过遍历列来实现,这更为方便。它使用示例中给出的现有列表。
file_32.5a
ROWS = 3
COLUMNS = 4
ELEMENTS = ROWS * COLUMNS
a = [
[5, 9, 3, 2],
[11, 12, 4, 1],
[10, 25, 22, 18]
]
k = 0 #这是新列表 b 的索引。
b = [None] * ELEMENTS
for j in range(COLUMNS): #遍历列
for i in range(ROWS):
b[k] = a[i][j]
k += 1
for k in range(ELEMENTS):
print(b[k], end = "\t")
除了使用新列表的索引 k,您还可以使用 append()方法,如下所示。
file_32.5b
ROWS = 3
COLUMNS = 4
ELEMENTS = ROWS * COLUMNS
a = [
[5, 9, 3, 2],
[11, 12, 4, 1],
[10, 25, 22, 18]
]
b = [] #创建一个完全空的列表
for j in range(COLUMNS): #遍历列
for i in range(ROWS):
b.append(a[i][j])
for k in range(ELEMENTS):
print(b[k], end = "\t")
32.6 从一维列表创建二维列表
为了更轻松地理解如何从一维列表创建二维列表,让我们用一个例子来说明。
编写一个 Python 程序,将一个包含 12 个元素的现有单维列表(如下所示)转换为一个 3 × 4 的二维列表,具体如下:单维列表的前三个元素必须放置在二维列表的第一列中,单维列表的下一个三个元素必须放置在二维列表的下一列中,依此类推。
下面是 12 个元素的单一列表和新的二维列表。

下面的 Python 程序创建新的二维列表,通过遍历列来实现,这更为方便。它使用示例中给出的现有列表。
file_32.6
ROWS = 3
COLUMNS = 4
a = [5, 11, 10, 9, 12, 25, 3, 4, 22, 2, 1, 18]
k = 0 #这是列表 a 的索引。
b = [[None] * COLUMNS for i in range(ROWS)]
for j in range(COLUMNS): #遍历列
for i in range(ROWS):
b[i][j] = a[k]
k += 1
for i in range(ROWS): #遍历行
for j in range(COLUMNS):
print(b[i][j], end = "\t")
print()
32.7 有用的数据结构函数/方法(子程序)
计算元素数量
len(structure_name)
您已经从之前章节中了解过这个函数!在第 14.3 节中,您了解到 len()函数返回字符串中的字符数。现在您需要了解的是,len()函数返回任何结构(如列表或字典)的元素数量!
示例
file_32.7a
a = [3, 6, 10, 12, 4, 2, 1]
print(len(a)) #它显示:7
length = len(a[2:4])
print(length) #它显示:2
for i in range(len(a)):
print (a[i], end = " ") #它显示:3 6 10 12 4 2 1
b = [
[5, 9, 3, 2],
[11, 12, 7, 1],
[10, 25, 22, 18]
]
print(len(b)) #它显示:3
print(len(b[0])) #它显示:4
在二维列表 b 中,len(b)函数返回 3(三行),而 len(b[0])返回 4(四列)。如果您想获取列表所有维度的元素总数,可以使用 product len(b) * len(b[0]),这将等于 12。
查找最大值
max(structure_name)
此函数返回列表或元组中的最大值。在字典的情况下,它返回最大的键。
示例
file_32.7b
a = [3, 6, 10, 2, 1, 12, 4]
print(max(a)) #它显示:12
maximum = max(a[1:4])
print(maximum) #它显示:10
b = [
[4, 6, 8],
[3, 11, 9],
[2, 9, 1]
]
print(max(b[1])) #它显示:11
c = ("Apollo", "Hermes", "Athena", "Aphrodite", "Dionysus")
print(max(c)) #它显示:Hermes
查找最小值
min(structure_name)
此函数返回列表或元组中的最小值。在字典的情况下,它返回最小的键。
示例
file_32.7c
a = [3, 6, 10, 2, 1, 12, 4]
print(min(a)) #它显示:1
minimum = min(a[1:4])
print(minimum) #它显示:2
b = [
[4, 6, 8],
[3, 11, 9],
[2, 9, 1]
]
print(min(b[0])) #它显示:4
c = ("Apollo", "Hermes", "Athena", "Aphrodite", "Dionysus")
print(min(c)) #它显示:Aphrodite
排序列表
排序是将列表中的元素按照一定顺序排列的过程。这里你有两个选择:你可以使用 sort() 方法对列表进行排序,或者你可以使用 sorted() 函数从一个初始列表中获取一个新的排序后的列表,同时保持初始列表不变。
使用 sort() 方法
list_name.sort([reverse = True])
此方法可以按升序或降序对列表进行排序。
示例
file_32.7d
a = [3, 6, 10, 2, 1, 12, 4]
a.sort()
print(a) #它显示:[1 2 3 4 6 10 12]
以降序排序
a.sort(reverse = True)
print(a) #它显示:[12 10 6 4 3 2 1]
b = [
[4, 6, 8],
[3, 11, 9],
[2, 9, 1]
]
对最后一行进行排序
b[2].sort()
for row in b:
for element in row:
print(element, end = "\t") #它显示:4 6 8
3 11 9
print() # 1 2 9
c = ["Hermes", "Apollo", "Dionysus"]
c.sort()
print(c) #它显示:['Apollo', 'Dionysus', 'Hermes']
sort() 方法不能与不可变数据类型一起使用,例如元组。
使用 sorted() 函数
sorted(structure_name [, reverse = True])
此函数返回一个新的排序后的列表或元组,可以是升序或降序,同时保持初始列表/元组不变。
示例
file_32.7e
a = [3, 6, 10, 2, 1, 12, 4]
b = sorted(a)
print(a) #它显示:[3, 6, 10, 2, 1, 12, 4]
print(b) #它显示:[1 2 3 4 6 10 12]
c = ["Hermes", "Apollo", "Dionysus"]
for element in sorted(c, reverse = True):
print(element, end = " ") #它显示:Hermes Dionysus Apollo
32.8 复习问题:对错
对以下每个陈述选择对或错。
1)逐行处理意味着每一行都是单独处理的,然后每一行的结果可以单独用于进一步处理。
2)以下代码片段在每列元素的总和小于 100 时显示单词“Okay”。
for i in range(ROWS):
total = 0
for j in range(COLUMNS):
total += a[i][j]
if total < 100: print("Okay")
3)逐列处理意味着每一列都是单独处理的,然后每一行的结果可以单独用于进一步处理。
4)以下代码片段显示了每列元素的总和。
total = 0
for j in range(COLUMNS):
for i in range(ROWS):
total += a[i][j]
print(total)
5)假设有 10 名学生,他们每人已经收到了五门课程的分数。根据这个信息,可以设计一个列表,其中行代表学生,列代表课程,但不能反过来,即行代表课程,列代表学生。
6)可以从二维列表创建一维列表,但不能反过来。
7)可以从三维列表创建一维列表。
8)以下两个代码片段显示相同的值。
a = [1, 6, 12, 2, 1]
print(len(a))
a = "Hello"
print(len(a))
9)以下代码片段显示三个值。
a = [10, 20, 30, 40, 50]
for i in range(3, len(a)):
print(a[i])
10)以下代码片段显示列表 b 中所有元素的值。
b = [10, 20, 30, 40, 50]
for i in range(len(b)):
print(i)
11)以下代码片段将列表 b 中所有元素的值加倍。
for i in range(len(b)):
b[i] *= 2
12)以下代码片段在屏幕上显示 30 的值。
a = [20, 50, 10, 30, 15]
print(max(a[2:len(a)]))
13)以下代码片段在屏幕上显示 50 的值。
a = [20, 50, 10, 30, 15]
b = [-1, -3, -2, -4, -1]
print(a[min(b)])
14)以下代码片段显示列表 b 的最小值。
b = [3, 6, 10, 2, 1, 12, 4]
b.sort()
print(b[0])
15)以下代码片段显示列表 b 的最小值。
b = [3, 1, 2, 10, 4, 12, 6]
print(sorted(a, reverse = True)[-1])
16)以下代码片段会抛出错误。
b = [3, 1, 2]
a = sort(b)
32.9 复习问题:多项选择题
选择以下每个陈述的正确答案。
1)以下代码片段
for i in range(ROWS):
total[i] = 0
for j in range(COLUMNS):
total[i] += a[i][j]
print(total[i])
a)显示每行的元素总和。
b)显示每列的元素总和。
c)显示列表中所有元素的总和。
d)以上都不是
2)以下代码片段
for j in range(COLUMNS):
total = 0
for i in range(ROWS):
total += a[i][j]
print(total)
a)显示每行的元素总和。
b)显示每列的元素总和。
c)显示列表中所有元素的总和。
d)以上都不是
3)以下代码片段
total = 0
for j in range(COLUMNS):
for i in range(ROWS):
total += a[i][j]
print(total)
a)显示每行的元素总和。
b)显示每列的元素总和。
c)显示列表中所有元素的总和。
d)以上都不是
4)以下代码片段
b = [None] * (ROWS * COLUMNS)
k = 0
for i in range(ROWS - 1, -1, -1):
for j in range(0, COLUMNS, -1):
b[k] = a[i][j]
k += 1
a)从二维列表创建一维列表。
b)从一维列表创建二维列表。
c)不满足确定性的性质
d)以上都不是
5)以下代码片段
b = [[None] * COLUMNS for i in range(ROWS)]
k = 0
for i in range(ROWS):
for j in range(COLUMNS - 1, -1, -1):
b[i][j] = a[k]
k += 1
a)从二维列表创建一维列表。
b)从一维列表创建二维列表。
c)以上皆非
6)以下两个代码片段
a = [3, 6, 10, 2, 4, 12, 1]
for i in range(7):
打印 a[i]
a = [3, 6, 10, 2, 4, 12, 1]
for i in range(len(a)):
打印 a[i]
a)产生相同的结果。
b)不会产生相同的结果。
c)以上皆非
7)以下两个代码片段
for i in range(len(a)):
打印 a[i]
for element in a:
打印 element
a)产生相同的结果。
b)不会产生相同的结果。
c)以上皆非
8)以下语句 min(b[1:len(b)])
a)返回列表 b 一部分的最低值。
b)返回列表 b 的最低值。
c)以上皆非
9)以下代码片段
a = [3, 6, 10, 1, 4, 12, 2]
打印 a[-min(a)]
a)在屏幕上显示 1 的值。
b)在屏幕上显示 3 的值。
c)在屏幕上显示 2 的值。
d)以上皆非
10)以下两个代码片段
for i in range(len(a)):
打印 sorted(a)[i]
for element in sorted(a):
打印 element
a)产生相同的结果,但左边的程序更快。
b)产生相同的结果,但右边的程序更快。
c)不会产生相同的结果。
d)以上皆非
11)以下三个代码片段
b.sort(reverse = True)
打印 b[0]
打印 sorted(b)[-1]
打印 max(b)
a)在屏幕上显示列表 b 的最大值。
b)在屏幕上显示列表 b 的最小值。
c)以上皆非
32.10 复习练习
完成以下练习。
1)有 15 名学生,他们每个人都已经收到了五次测试的成绩。编写一个 Python 程序,让用户输入每个学生在所有测试中的成绩(作为百分比)。然后,为每个学生计算平均成绩,并根据以下表格显示为字母等级。
| Grade | Percentage |
|---|---|
| A | 90 - 100 |
| B | 80 - 89 |
| C | 70 - 79 |
| D | 60 - 69 |
| E / F | 0 - 59 |
2)在地球上,自由落体的加速度为 9.81 m/s²向下。这个值用 g 表示。一名学生想通过实验计算这个值。她允许五个不同的物体从已知高度落下,并测量它们到达地板所需的时间。她为每个物体做了 10 次。然后,使用公式,她为每个物体、每次落下计算 g 值。但由于她的计时器不够准确,她需要一个 Python 程序,让她输入所有计算出的 g 值在一个 5 × 10 的列表中,然后计算并显示
a)对于每个物体,g 的平均值
b)对于每个秋季,g 的平均值
c)g 的整体平均值
3)一个有 15 名球员的篮球队打了 12 场比赛。编写一个 Python 程序,让用户输入每个球员在每场比赛中得分的数量。然后程序必须显示
a)对于每个球员,总得分
b)对于每场比赛,得分的总数
4)编写一个 Python 程序,让用户输入 20 个城市在一天内每小时测量的温度,然后显示所有城市的平均温度低于 10 华氏度的时段。
5)在一个足球锦标赛中,一支有 24 名球员的足球队进行了 10 场比赛。编写一个 Python 程序,让用户为每位球员输入姓名以及他们在每场比赛中进球的数量。然后程序必须显示
a)对于每位球员,他的姓名和他平均进球数
b)对于每场比赛,比赛的索引编号(1、2、3 等等)和总进球数
6)有 12 名学生,他们每人已经完成了六节课的评分。编写一个 Python 程序,让用户输入学生的姓名以及他们在所有课程中的成绩,然后显示
a)对于每位学生,他们的姓名和平均成绩
b)对于每节课,平均成绩
c)平均成绩低于 60 分的学生的姓名
d)平均成绩高于 89 分的学生的姓名,旁边还有“Bravo!”的消息
假设用户输入的值在 0 到 100 之间有效。
7)在一场歌唱比赛中,每位艺术家唱一首自己选择的歌曲。有五位评委和 15 位艺术家,每位艺术家的表演都会得到评分。编写一个 Python 程序,提示用户输入评委的姓名、艺术家的姓名、每位艺术家唱的歌曲标题以及每位评委给出的分数。然后程序必须显示
a)对于每位艺术家,他们的姓名、歌曲标题和他们的总分
b)对于每位评委,他们的姓名和他们给出的平均分数
8)身体质量指数(BMI)常用于确定一个人是否因身高而超重或体重不足。计算 BMI 使用的公式是
.
编写一个 Python 程序,让用户输入 30 个人的体重(以磅为单位)和身高(以英寸为单位),这些数据是按月度测量的,为期一年(从一月到十二月)。然后程序必须计算并显示
a)对于每个人,他们的平均体重、平均身高和平均 BMI
b)对于每个人,他们在五月份和八月份的 BMI
请注意,所有人都是成年人,但其中一些人的年龄在 18 到 25 岁之间。这意味着他们可能还会长高,因此他们的身高可能会每个月都不同!
9)编写一个 Python 程序,让用户输入 1000 个消费者在一个月开始和结束时的电表读数(千瓦时,kWh)。然后程序必须计算并显示
a)对于每位消费者,消耗的千瓦时数和根据每千瓦时 0.07 美元的价格以及 19%的增值税(VAT)率必须支付的费用
b)总消耗量和必须支付的总金额。
- 编写一个 Python 程序,提示用户输入美元金额,并计算并显示相应的欧元、英镑、澳大利亚元和加拿大元的货币价值。下表包含每种货币在五个工作日内的汇率。程序必须计算每种货币的平均价值,并基于该平均价值进行转换。

- 毛收入取决于工资率和每周工作的小时总数。然而,如果有人工作超过 40 小时,他们将获得加班费,即超过 40 小时的所有工作时间的 1.5 倍。编写一个 Python 程序,让用户输入工资率以及 10 名员工的姓名和每天(周一至周五)工作的小时数。然后程序必须计算并显示
a) 加班员工的姓名
b) 对于每位员工,显示他们的名字和平均每日毛收入
c) 所有员工的总毛收入
d) 对于每位员工,显示他们的名字、加班工作(超过 8 小时)的日期,以及信息“加班!”
e) 对于每一天,显示日期和总毛收入
- 编写一个 Python 程序,从以下二维列表创建一个包含 12 个元素的列表,如下所示:二维列表的第一行必须放置在一维列表的前四个位置,二维列表的第二行必须放置在一维列表的下一个四个位置,二维列表的最后一行必须放置在一维列表的最后四个位置。
.
- 编写一个 Python 程序,从以下一维列表创建一个 3 × 3 的列表,如下所示:一维列表的前三个元素必须放置在二维列表的最后一行,一维列表的下一个三个元素必须放置在二维列表的第二行,一维列表的最后三个元素必须放置在二维列表的第一行。

第三十三章
更多关于数据结构的内容
33.1 列表简单练习
练习 33.1-1 创建包含相邻元素平均值的列表
编写一个 Python 程序,让用户输入 100 个正数值到一个列表中。然后,程序必须创建一个包含 98 个元素的新列表 newArr。这个新列表的每个位置必须包含用户提供的列表中当前和下两个位置的三个元素的平均值。
解决方案
让我们通过使用 10 个元素的示例来尝试理解这个练习。

List newArr 是创建的新列表。在列表 newArr 中,位置 0 的元素是列表 a 中当前和下两个位置的元素的平均值;即 (5 + 10 + 9) / 3 = 8。位置 1 的元素是列表 a 中当前和下两个位置的元素的平均值;即 (10 + 9 + 2) / 3 = 7,以此类推。
Python 程序如下。
file_33.1-1a
ELEMENTS_OF_A = 100
ELEMENTS_OF_NEW = ELEMENTS_OF_A - 2
a = [None] * ELEMENTS_OF_A
for i in range(ELEMENTS_OF_A):
a[i] = float(input())
newArr = [None] * ELEMENTS_OF_NEW
for i in range(ELEMENTS_OF_NEW):
newArr[i] = (a[i] + a[i + 1] + a[i + 2]) / 3
for i in range(ELEMENTS_OF_NEW):
print(newArr[i])
如果你更喜欢更 Pythonic 的方式,解决方案如下。
file_33.1-1b
from math import fsum
ELEMENTS_OF_A = 100
a = []
for i in range(ELEMENTS_OF_A):
a.append(float(input()))
newArr = []
for i in range(ELEMENTS_OF_A - 2):
newArr.append(fsum(a[i:i + 3]) / 3)
for element in newArr:
print(element)
练习 33.1-2 创建包含最大值的列表
编写一个 Python 程序,让用户输入到列表 a 和 b 中各 20 个数值。然后,程序必须创建一个包含 20 个元素的新列表 newArr。新列表的每个位置必须包含列表 a 和 b 对应位置的较大值。
解决方案
没有什么新的!你需要两个 for 循环来读取列表 a 和 b 的值,一个用于创建列表 newArr,另一个用于在屏幕上显示列表 newArr。
Python 程序如下所示。
file_33.1-2
ELEMENTS = 20
读取列表 a 和 b
a = [None] * ELEMENTS
b = [None] * ELEMENTS
for i in range(ELEMENTS):
a[i] = float(input())
for i in range(ELEMENTS):
b[i] = float(input())
创建列表 newArr
newArr = [None] * ELEMENTS
for i in range(ELEMENTS):
if a[i] > b[i]:
newArr[i] = a[i]
else:
newArr[i] = b[i]
显示列表 newArr
for i in range(ELEMENTS):
print(newArr[i])
练习 33.1-3 合并一维列表
编写一个 Python 程序,对于两个分别包含 10 和 15 个元素的列表 a 和 b,创建一个包含 25 个元素的新列表 newArr。这个新列表的前 10 个位置必须包含列表 a 的元素,接下来的 15 个位置包含列表 b 的元素。
解决方案
由于并非所有计算机语言在数据结构领域都像 Python 那样强大,所以我们在这里研究三种方法。第一种方法可以应用于大多数计算机语言,而第二种和第三种方法则更具有 Python 风格!
第一种方法
正如你在下面的示例中可以看到的,列表 a 和列表 newArr 的元素索引位置之间存在直接的对应关系。具体来说,列表 a 的位置 0 的元素存储在列表 newArr 的位置 0,列表 a 的位置 1 的元素存储在列表 newArr 的位置 1,依此类推。然而,这种对应关系对于列表 b 不成立;它的元素需要在 newArr 中偏移 10 个位置。

为了将列表 a 的值赋给列表 newArr,你可以使用以下代码片段。
for i in range(len(a)):
newArr[i] = a[i]
然而,为了将列表 b 的值赋给列表 newArr,你的代码片段应该略有不同,如下所示。
for i in range(len(b)):
newArr[len(a) + i] = b[i]
最终的 Python 程序如下。
file_33.1-3a
创建列表 a 和 b
a = [5, 10, 9, 6, 7, -6, 13, 12, 11, 2]
b = [-11, 25, 4, 45, 67, 87, 34, 23, 33, 55, 13, 15, -4, -2, -22]
创建列表 newArr
newArr = [None] * (len(a) + len(b))
for i in range(len(a)):
newArr[i] = a[i]
for i in range(len(b)):
newArr[len(a) + i] = b[i]
显示列表 newArr
for i in range(len(newArr)):
print(newArr[i], end = "\t")
第二种方法
这种方法使用了 Python 的 append()方法。
file_33.1-3b
创建列表 a 和 b
a = [5, 10, 9, 6, 7, -6, 13, 12, 11, 2]
b = [-11, 25, 4, 45, 67, 87, 34, 23, 33, 55, 13, 15, -4, -2, -22]
创建列表 newArr
newArr = []
for element in a:
newArr.append(element)
for element in b:
newArr.append(element)
显示列表 newArr
for i in range(len(newArr)):
print(newArr[i], end = "\t")
第三种方法
这种方法利用了 Python 的魔法力量。在 Python 中,为了合并两个列表,你可以简单地使用连接运算符(+)。Python 会为你完成剩下的工作!
file_33.1-3c
创建列表 a 和 b
a = [5, 10, 9, 6, 7, -6, 13, 12, 11, 2]
b = [-11, 25, 4, 45, 67, 87, 34, 23, 33, 55, 13, 15, -4, -2, -22]
创建列表 newArr
newArr = a + b
显示列表 newArr
for i in range(len(newArr)):
print(newArr[i], end = "\t")
练习 33.1-4 创建两个列表 - 分离正负值
编写一个 Python 程序,让用户输入 100 个数值到列表中,然后创建两个新的列表,pos 和 neg。列表 pos 必须包含正数,而列表 neg 必须包含负数。值 0(如果有)不得添加到最终的列表 pos 或 neg 中。
解决方案
有两种方法!第一种可以应用于大多数计算机语言,而第二种则更具有 Python 风格!
第一种方法
让我们通过以下示例来分析这种方法。

在这个练习中,列表 ar 的元素索引与列表 pos 和 neg 的索引位置之间没有一一对应的关系。例如,列表 ar 的位置 1 的元素不会存储在列表 neg 的位置 1,或者列表 ar 的位置 2 的元素不会存储在列表 pos 的位置 2。因此,你不能这样做,
for i in range(ELEMENTS):
if ar[i] > 0:
pos[i] = ar[i]
elif ar[i] < 0:
neg[i] = ar[i]
因为它将导致以下两个列表。

你在这里需要两个独立的索引变量:posIndex 用于列表 pos,negIndex 用于列表 neg。这些索引变量必须独立增加,并且只有在将元素添加到相应的列表时才增加。索引变量 posIndex 只有在将元素添加到列表 pos 时才增加,索引变量 negIndex 只有在将元素添加到列表 neg 时才增加,如下面的代码片段所示。
posIndex = 0
negIndex = 0
for i in range(ELEMENTS):
if ar[i] > 0:
pos[posIndex] = ar[i]
posIndex += 1
elif ar[i] < 0:
neg[negIndex] = ar[i]
negIndex += 1
注意,变量 posIndex 和 negIndex 具有双重角色。当循环迭代时,它们都指向必须放置新元素的下一位。但是当循环迭代完成后,变量 posIndex 和 negIndex 也包含了每个对应列表中的元素总数!
完整的解决方案将在下面展示。
file_33.1-4a
ELEMENTS = 100
ar = [None] * ELEMENTS
for i in range(ELEMENTS):
ar[i] = float(input())
创建列表 pos 和 neg
posIndex = 0
negIndex = 0
pos = [None] * ELEMENTS
neg = [None] * ELEMENTS
for i in range(ELEMENTS):
if ar[i] > 0:
pos[posIndex] = ar[i]
posIndex += 1
elif ar[i] < 0:
neg[negIndex] = ar[i]
negIndex += 1
for i in range(posIndex):
print(pos[i], end = "\t")
print()
for i in range(negIndex):
print(neg[i], end = "\t")
注意,列表 pos 和 neg 分别包含 posIndex 和 negIndex 个元素。这就是为什么两个最后的循环控制结构迭代直到变量 i 达到 posIndex - 1 和 negIndex - 1 的值,而不是像你可能错误预期的那样达到 ELEMENTS - 1。
第二种方法
第一种方法的主要缺点是!列表 pos 和 neg 必须初始化为 100 的大小,因为列表 ar 可能只包含正数,或者可能只包含负数。使用 append()方法,第二种方法比前一种方法更有效,因为它使用了最少的内存(RAM)。
file_33.1-4b
ELEMENTS = 100
ar = []
for i in range(ELEMENTS):
ar.append(float(input()))
创建列表 pos 和 neg
pos = []
neg = []
for element in ar:
if element > 0:
pos.append(element)
elif element < 0:
neg.append(element)
for element in pos:
print(element, end = "\t")
print()
for element in neg:
print(element, end = "\t")
显然,一个问题可以有多个解决方案。取决于你找到最优解!
小心!在 Python 中,尽管你可以做类似 x = y = 0 的事情,但这在列表中并不成立。写下 pos = neg = []是完全错误的,因为 pos 和 neg 都会指向同一个列表。你必须使用两个单独的语句,就像这个方法中展示的那样。
练习 33.1-5 创建包含数字 5 的列表
编写一个 Python 程序,让用户输入 100 个两位整数到列表中,然后创建一个新列表,其中只包含包含至少一个数字 5 的整数。
解决方案
这个练习需要一些过去的知识。在练习 13.1-2 中,你学习了如何使用商和余数将整数分解为其各个位数。在这里,用户提供的整数有两位数;因此,你可以使用以下代码片段来分解变量 x 中包含的任何两位数整数。
lastDigit = x % 10
firstDigit = x // 10
或者更符合 Python 风格的方式是使用 divmod()函数。
firstDigit, lastDigit = divmod(x, 10)
有两种方法你可以用来解决这个练习!第一种方法可以应用于大多数计算机语言,而第二种方法则更符合 Python 风格!
第一种方法
这种方法使用一个额外的变量作为新列表的索引。当你想要使用旧列表的值创建一个新列表,并且它们的索引位置没有一一对应关系时,这是必要的。当然,这个变量只有在向新列表添加新元素时才增加 1。此外,当创建新列表的循环完成迭代后,这个变量的值也匹配新列表中的元素总数!最终的 Python 程序如下。
file_33.1-5a
ELEMENTS = 100
a = [None] * ELEMENTS
for i in range(ELEMENTS):
a[i] = int(input())
k = 0
b = [None] * ELEMENTS
for i in range(ELEMENTS):
lastDigit = a[i] % 10
firstDigit = a[i] // 10
if firstDigit == 5 or lastDigit == 5:
b[k] = a[i]
k += 1
for i in range(k):
print(b[i], end = "\t")
第二种方法
如前一个练习所示,第一种方法的主要缺点是它将列表 b 初始化为 100 的大小,而不管用户是否输入包含至少一个 5 的数字!使用 append()方法,第二种方法更高效,也更符合 Python 风格!
file_33.1-5b
ELEMENTS = 100
a = []
for i in range(ELEMENTS):
a.append(int(input()))
b = []
for element in a:
firstDigit, lastDigit = divmod(element, 10)
if 5 in [firstDigit, lastDigit]:
b.append(element)
for element in b:
print(element, end = "\t")
33.2 使用列表进行数据验证
如你已在 第 29.3 节 中所学,你可以使用三种方法来验证数据输入。你的方法将取决于你是否希望显示错误信息,以及你是否希望为每种输入错误显示不同的错误信息,或者只是显示通用的错误信息。
第一种方法 – 无错误信息验证数据输入
在 第 29.3 节 中,你学习了如何不显示任何错误信息来验证用户输入的单个值。为了方便起见,以下再次给出一般形式的代码片段。
while True:
输入数据 = input("提示信息")
if 输入数据测试 1 成功并
输入数据测试 2 成功并
…
输入数据测试 N 成功:break
你还记得它是如何工作的吗?如果用户输入一个有效的值,执行流程将简单地继续到程序的下一部分。然而,如果他们输入一个无效的值,主要目标是反复提示他们,直到他们最终提供一个有效的值。
当将数据输入列表时,你可以使用相同的原则。如果你使用 for 循环迭代列表的所有元素,代码片段将如下所示。
input_list = [None] * ELEMENTS
for i in range(ELEMENTS)
while True:
输入数据 = input("提示信息")
if 输入数据测试 1 成功并
输入数据测试 2 成功并
…
输入数据测试 N 成功:break
input_list[i] = 输入数据
如你所见,当执行流程退出嵌套的测试后循环结构时,变量 input_data 一定包含一个有效的值,该值随后被分配到列表 input_list 的一个元素中。然而,同样的过程可以更简单地实现,而不需要使用额外的变量 input_data,如下所示。
input_list = [None] * ELEMENTS
for i in range(ELEMENTS):
while True:
input_list[i] = input("提示信息")
if input_list[i] 测试 1 成功并
input_list[i] 测试 2 成功并
…
input_list[i] 测试 N 成功:break
第二种方法 – 使用通用错误信息验证数据输入
如前所述,下一个代码片段取自 第 29.3 节 并调整为与列表一起工作。它验证数据输入并为任何类型的输入错误显示通用的错误信息(即,任何类型输入错误的相同错误信息)。
input_list = [None] * ELEMENTS
for i in range(ELEMENTS):
input_list[i] = input("提示信息")
当 input_list[i] 测试 1 失败或
input_list[i] 测试 2 失败或
…
input_list[i] 测试 N 失败:
print("错误信息")
input_list[i] = input("提示信息")
第三种方法 – 使用不同错误信息验证数据输入
再次,下一个代码片段取自 第 29.3 节 并调整为与列表一起工作。它验证数据输入并为每种输入错误显示不同的错误信息。
input_list = [None] * ELEMENTS
for i in range(ELEMENTS):
while True:
input_list[i] = input("提示消息")
failure = False
if input_list[i] test 1 fails:
print("错误消息 1")
failure = True
elif input_list[i] test 2 fails:
print("错误消息 2")
failure = True
elif …
…
elif input_list[i] test N fails:
print("错误消息 N")
failure = True
if not failure: break
练习 33.2-1 以相反顺序显示奇数
编写一个 Python 程序,提示用户输入 20 个奇数正整数到一个列表中,然后以它们提供的顺序的相反顺序显示它们。程序必须验证数据输入,防止用户输入非正值、浮点数或偶数整数。以三种版本解决这个练习:
a)在没有显示任何错误消息的情况下验证数据输入。
b)验证数据输入并显示一个通用的错误消息。
c)验证数据输入并显示不同类型的输入错误的不同错误消息。
解决方案
在第 33.2 节中学习到的所有三种验证数据输入的方法都将在此处展示。让我们首先在不进行数据验证的情况下解决这个练习。
ELEMENTS = 20
odds = [None] * ELEMENTS
for i in range(ELEMENTS):
x = int(input("输入一个奇数正整数: ")) [更多…]
odds[i] = x
以相反的顺序显示元素 # 或者你可以这样做
for i in range(ELEMENTS - 1, -1, -1): # for element in odds[::-1]:
print(odds[i], end = "\t") # print(element, end = "\t")
无错误消息的验证
要在没有显示任何错误消息的情况下验证数据输入,请使用第 33.2 节中的第一种方法。只需将带有虚线矩形的语句替换为以下代码片段。
while True:
x = float(input("输入一个奇数正整数: "))
if x > 0 and x == int(x) and x % 2 != 0: break
odds[i] = int(x)
最终程序如下
file_33.2-1a
ELEMENTS = 20
odds = [None] * ELEMENTS
for i in range(ELEMENTS):
while True: [更多…]
x = float(input("输入一个奇数正整数: "))
if x > 0 and x == int(x) and x % 2 != 0: break
odds[i] = int(x)
以相反的顺序显示元素 # 或者你可以这样做
for i in range(ELEMENTS - 1, -1, -1): # for element in odds[::-1]:
print(odds[i], end = "\t") # print(element, end = "\t")
变量 x 必须是浮点型。这是为了允许用户输入整数或浮点数(实数)。
带有通用错误消息的验证
要验证数据输入并显示一个通用的错误消息,请将带有虚线矩形的语句替换为第 33.2 节中第二种方法的基础代码片段。Python 程序如下。
file_33.2-1b
ELEMENTS = 20
odds = [None] * ELEMENTS
for i in range(ELEMENTS):
x = float(input("请输入一个奇数正整数: ")) [更多…]
while x <= 0 or x != int(x) or x % 2 == 0:
print("无效值!")
x = float(input("请输入一个奇数正整数: "))
odds[i] = int(x)
反向显示元素
for element in odds[::-1]:
print(element, end = "\t")
使用不同的错误信息进行验证
这里,替换的代码片段基于第 33.2 节的第三种方法。为了验证数据输入并显示针对每种输入错误的错误信息,Python 程序如下。
file_33.2-1c
ELEMENTS = 20
odds = [None] * ELEMENTS
for i in range(ELEMENTS):
while True: [更多…]
x = float(input("请输入一个奇数正整数: "))
failure = False
if x <= 0 :
print("无效值:输入了非正数!")
failure = True
elif x != int(x):
print("无效值:输入了浮点数!")
failure = True
elif x % 2 == 0:
print("无效值:输入了偶数!")
failure = True
if not failure: break
odds[i] = int(x)
反向显示元素
for element in odds[::-1]:
print(element, end = "\t")
33.3 在列表中查找最小和最大值
这是本书中第三次也是最后一次讨论这个主题。第一次是在第 22.2 节中使用决策控制结构,第二次是在第 29.4 节中使用循环控制结构。所以,除了当你想要找到已包含一些值的数据结构的最小或最大值时,不需要担心变量最小或最大值的初始值,因为你可以直接将数据结构的第一个元素的值赋给它们之外,没有太多可讨论的。
练习 33.3-1 哪个深度最大?
编写一个 Python 程序,允许用户输入 20 个湖泊的深度,然后显示最深的一个。
解决方案
在用户输入列表 depths 中 20 个湖泊的深度后,变量 maximum 的初始值可以设置为 depths[0]的值,即列表 depths 的第一个元素。然后程序可以搜索从索引 1 开始的任何后续值,只要这个值大于这个值。最终的解决方案非常直接,下面将不进行进一步解释。
file_33.3-1a
LAKES = 20
depths = [None] * LAKES
for i in range(LAKES):
depths[i] = float(input())
maximum = depths[0] #初始值
从索引 1 开始搜索
for i in range(1, LAKES):
if depths[i] > maximum:
maximum = depths[i]
print(maximum)
虽然从位置 0 开始迭代而不是 1 可能不会出错,但程序将执行一次无用的迭代。
将变量 maximum 分配一个“几乎任意”的初始值并没有错,但这样做没有理由。第一个元素的值就很好!如果你坚持的话,你可以分配一个初始值为 0,因为地球上没有湖泊的深度是负数。
但是,请注意,找到列表中最大值的更 Pythonic 方法是使用 max() 函数,如下所示。
file_33.3-1b
LAKES = 20
depths = []
for i in range(LAKES):
depths.append(float(input()))
maximum = max(depths)
print(maximum)
相应地,如果你想找到列表中最小值,你可以使用 min() 函数。
练习 33.3-2 哪个湖泊最深?
编写一个 Python 程序,让用户输入 20 个湖泊的名称和深度,然后显示最深湖泊的名称。
解决方案
如果你不知道如何找到最深湖泊的名称,你可能需要通过重新阅读 练习 29.4-2 来刷新你的记忆。
在这个练习中,你需要两个一维列表:一个用于存储湖泊的名称,另一个用于存储湖泊的深度。解决方案如下。
file_33.3-2
LAKES = 20
names = [None] * LAKES
depths = [None] * LAKES
for i in range(LAKES):
names[i] = input()
depths[i] = float(input())
maximum = depths[0]
mName = names[0]
for i in range(1, LAKES):
if depths[i] > maximum:
maximum = depths[i]
mName = names[i]
print(mName)
在这个练习中,你不能使用 max() 函数!它会返回最大的深度,而不是那个最深湖泊的名称!
练习 33.3-3 哪个湖泊,在哪个国家,拥有哪个平均面积,是最深的?
编写一个 Python 程序,让用户输入 20 个湖泊的名称、深度以及它们所属的国家和平均面积。程序必须然后显示关于最深湖泊的所有可用信息。
解决方案
让我们看看下一个例子,六个湖泊的深度以英尺表示,平均面积以平方英里表示。

显然,贝加尔湖保持着最深湖泊的记录,位于索引 2。如果你要以类似于上一个练习(练习 33.3-2)的方式来做这个练习,你需要三个额外的变量来保存每次找到比之前存储的深度更大的深度时的名称、国家和面积。然而,下面提供的解决方案采用了一种更有效的方法,只使用一个变量(indexOfMax)来跟踪这些值所在的索引。
file_33.3-3
LAKES = 20
names = [None] * LAKES
depths = [None] * LAKES
countries = [None] * LAKES
areas = [None] * LAKES
for i in range(LAKES):
names[i] = input()
depths[i] = float(input())
countries[i] = input()
areas[i] = float(input())
找到最大深度及其索引
maximum = depths[0]
indexOfMax = 0
for i in range(1, LAKES):
if depths[i] > maximum:
maximum = depths[i]
indexOfMax = i
使用 indexOfMax 作为索引显示信息
print(depths[indexOfMax], names[indexOfMax], end = " ")
print(countries[indexOfMax], areas[indexOfMax])
将变量 indexOfMax 的初始值设置为 0 是必要的,因为总是有可能最大值确实存在于位置 0。
练习 33.3-4 哪些学生获得了最高分?
编写一个 Python 程序,提示用户输入 200 名学生的姓名和成绩,然后显示所有那些共享一个最高分的学生姓名。使用循环控制结构,程序还必须验证数据输入,并在用户输入空姓名或任何负值或大于 100 的成绩时显示错误信息。
解决方案
在这个练习中,你需要验证姓名和成绩。一个以通用形式给出的代码片段显示了数据输入阶段。
STUDENTS = 200
names = [None] * STUDENTS
grades = [None] * STUDENTS
for i in range(STUDENTS):
提示用户输入一个姓名并验证它。它不能为空!
提示用户输入一个分数并验证它。它不能为负或大于 100。
在数据输入阶段之后,循环控制结构必须搜索最大值,然后,另一个循环控制结构必须搜索列表 grades 中所有等于该最大值的值。
下一个展示解决方案。
file_33.3-4
STUDENTS = 200
names = [None] * STUDENTS
grades = [None] * STUDENTS
for i in range(STUDENTS):
提示用户输入一个姓名并验证它。
names[i] = input("为第 " + str(i + 1) + " 号学生输入姓名:")
while names[i] == "":
print("错误!姓名不能为空!")
names[i] = input("为第 " + str(i + 1) + " 号学生输入姓名:")
提示用户输入一个分数并验证它。
grades[i] = int(input("输入他们的成绩:"))
while grades[i] < 0 or grades[i] > 100:
print("无效值!") #
grades[i] = int(input("输入他们的成绩:"))
寻找最高分
maximum = grades[0] # 或者你也可以这样做:
for i in range(1, STUDENTS): # maximum = max(grades)
if grades[i] > maximum: #
maximum = grades[i] #
显示所有共享一个最高分的学生姓名
print("以下学生获得了最高分:")
for i in range(STUDENTS):
if grades[i] == maximum:
print(names[i])
请注意,没有使用列表,这个练习是无法解决的。
请注意,以下代码片段也是正确的,但非常低效。
print("以下学生获得了最高分:")
for i in range(STUDENTS):
if grades[i] == max(grades):
print(names[i])
原因是,在这个例子中,函数 max() 在每次循环迭代时都会被调用——即 200 次!
练习 33.3-5 寻找二维列表的最小值
编写一个 Python 程序,让用户输入一月份在 10 个不同城市记录的同一小时每天的温度(华氏度)。Python 程序必须显示最低温度。
解决方案
在这个练习中,你需要以下列表。

列表 t 有 31 列(0 到 30),与一月份的天数一样多。
这里没有什么新的内容。变量 minimum 的初始值可以是元素 t[0][0] 的值。然后,程序可以遍历行,甚至列,以查找最小值。解决方案将在下面展示。
file_33.3-5a
CITIES = 10
DAYS = 31
读取列表 t
t = [[None] * DAYS for i in range(CITIES)]
for i in range(CITIES):
for j in range(DAYS):
t[i][j] = int(input())
寻找最小值
minimum = t[0][0]
for i in range(CITIES):
for j in range(DAYS):
if t[i][j] < minimum:
minimum = t[i][j]
print(minimum)
在这个练习中,你不能这样做,因为如果你这样做,并且变量 j 从 1 开始,那么索引为 0 的整个列将不会被检查!
寻找最小值
minimum = t[0][0]
for i in range(CITIES):
for j in range(1, DAYS): #这是错误的!变量 j 必须从 0 开始
if t[i][j] < minimum:
minimum = t[i][j]
虽然如此,但更 Pythonic 的方法是通过 min() 函数找到列表 t 的最低值,如下所示。
file_33.3-5b
CITIES = 10
DAYS = 31
读取列表 t
t = []
for i in range(CITIES):
t.append([])
for j in range(DAYS):
t[i].append(int(input()))
print(min(t))
练习 33.3-6 寻找最冷的一天所在的城市
编写一个 Python 程序,让用户输入 10 个城市的名称以及这些城市一月份每天记录的同一小时温度(华氏度)。Python 程序必须显示温度最低的城市及其记录的日期。
解决方案
在这个练习中,需要以下两个列表。

解决方案很简单。每次变量 minimum 更新其值时,两个变量 m_i 和 m_j 可以分别保存变量 i 和 j 的当前值。最后,这两个变量将包含最小值存在的位置的行索引和列索引。解决方案如下。
file_33.3-6
CITIES = 10
DAYS = 31
names = [None] * CITIES
t = [[None] * DAYS for i in range(CITIES)]
for i in range(CITIES):
names[i] = input()
for j in range(DAYS):
t[i][j] = int(input())
minimum = t[0][0]
m_i = 0
m_j = 0
for i in range(CITIES):
for j in range(DAYS):
if t[i][j] < minimum:
minimum = t[i][j]
m_i = i
m_j = j
print("最低温度:", minimum)
print("城市:", names[m_i])
print("日期:", m_j + 1)
将变量 m_i 和 m_j 的初始值设置为 0 是必要的,因为总是有可能最小值是元素 t[0][0] 的值。
练习 33.3-7 查找每行的最小和最大值
编写一个 Python 程序,允许用户输入列表 b 的 20 × 30 元素值,然后查找并显示每行的最小和最大值。
解决方案
实际上,有三种方法。第一种方法创建两个辅助的一维列表,最小值和最大值,然后显示它们。列表最小值和最大值将分别包含每个位置上的每行的最小和最大值。另一方面,第二种和第三种方法查找并直接显示每行的最小和最大值。让我们研究一下它们。
第一种方法 – 创建辅助列表
为了更好地理解这种方法,让我们使用“从内到外”的方法。当以下代码片段完成迭代时,辅助的一维列表最小值和最大值将分别包含位置 0 上的列表 b 的第一行(行索引 0)的最小和最大值。假设变量 i 包含值 0。
minimum[i] = b[i][0]
maximum[i] = b[i][0]
for j in range(1, COLUMNS):
if b[i][j] < minimum[i]:
minimum[i] = b[i][j]
if b[i][j] > maximum[i]:
maximum[i] = b[i][j]
请注意,变量 j 从 1 开始。虽然程序会多执行一次无用的迭代,但从列索引 0 开始迭代也不会错。
现在一切都已阐明,为了处理整个列表 b,你只需将前面的代码片段嵌套在一个遍历所有行的 for 循环中,如下所示。
for i in range(ROWS):
minimum[i] = b[i][0]
maximum[i] = b[i][0]
for j in range(1, COLUMNS):
if b[i][j] < minimum[i]:
minimum[i] = b[i][j]
if b[i][j] > maximum[i]:
maximum[i] = b[i][j]
最终的 Python 程序如下。
file_33.3-7a
ROWS = 30
COLUMNS = 20
b = [[None] * COLUMNS for i in range(ROWS)]
for i in range(ROWS):
for j in range(COLUMNS):
b[i][j] = float(input())
minimum = [None] * ROWS
maximum = [None] * ROWS
for i in range(ROWS):
minimum[i] = b[i][0]
maximum[i] = b[i][0]
for j in range(1, COLUMNS):
if b[i][j] < minimum[i]:
minimum[i] = b[i][j]
if b[i][j] > maximum[i]:
maximum[i] = b[i][j]
for i in range(ROWS):
print(minimum[i], maximum[i])
第二种方法 – 查找并直接显示最小和最大值
让我们再次使用“从内到外”的方法。下面的代码片段查找并直接显示列表 b 的第一行(行索引 0)的最小和最大值。假设变量 i 包含值 0。
minimum = b[i][0]
maximum = b[i][0]
for j in range(1, COLUMNS):
if b[i][j] < minimum:
minimum = b[i][j]
if b[i][j] > maximum:
maximum = b[i][j]
print(minimum, maximum)
为了处理整个列表 b,你可以将此代码片段嵌套在一个遍历所有行的 for 循环中,如下所示。
for i in range(ROWS):
minimum = b[i][0]
maximum = b[i][0]
for j in range(1, COLUMNS):
if b[i][j] < minimum:
minimum = b[i][j]
if b[i][j] > maximum:
maximum = b[i][j]
print(minimum, maximum)
最终的 Python 程序如下。
file_33.3-7b
ROWS = 30
COLUMNS = 20
b = [[None] * COLUMNS for i in range(ROWS)]
for i in range(ROWS):
for j in range(COLUMNS):
b[i][j] = float(input())
for i in range(ROWS):
minimum = b[i][0]
maximum = b[i][0]
for j in range(1, COLUMNS):
if b[i][j] < minimum:
minimum = b[i][j]
if b[i][j] > maximum:
maximum = b[i][j]
print(minimum, maximum)
第三种方法 – Pythonic 方法
此方法使用 Python 的 min() 和 max() 函数。它找到并直接显示每行的最低和最高值。
file_33.3-7c
ROWS = 30
COLUMNS = 20
b = []
for i in range(ROWS):
b.append([])
for j in range(COLUMNS):
b[i].append(float(input()))
for row in b:
print(min(row), max(row))
33.4 排序列表
排序算法是计算机科学中的一个重要主题。排序算法是一种将列表中的元素按特定顺序排列的算法。有许多排序算法,每个算法都有其特定的优点和缺点。
大多数排序算法通过比较列表中的元素来工作。它们通常通过效率和内存需求来评估。
有许多排序算法。其中一些是:
►冒泡排序算法
►改进的冒泡排序算法
►选择排序算法
►插入排序算法
►堆排序算法
►归并排序算法
►快速排序算法
关于它们的效率,冒泡排序算法被认为是效率最低的,而列表中的每个后续算法都比前一个算法表现更好。快速排序算法被认为是最好的和最快的排序算法之一,尤其是在大规模数据处理方面。
排序算法不仅可以用于显示数据按升序或降序排列,还可以帮助从一组给定值中找到最小和最大值。例如,在一个升序排列的列表中,最小值存在于第一个索引位置,最大值存在于最后一个索引位置。仅为了找到最小和最大值而排序列表是非常低效的,但如果程序为了其他原因而排序列表,并且随后需要最小或最大值,你知道在哪里可以找到它们!
你可能需要排序算法的另一个场景是当你想要找到并显示列表中的三个最大(或最小)数字时。在这种情况下,你可以按降序排序列表,然后只显示位于索引位置 0、1 和 2 的前三个元素。
如你所知,Python 包含了用于列表排序的 sort()方法和 sorted()函数。然而,在某些情况下,需要实现自定义排序算法,尤其是在需要保留与第二个列表元素一对一对应关系的同时对列表进行排序时。
练习 33.4-1 冒泡排序算法——使用数值对一维列表进行排序
编写一个 Python 程序,让用户输入 20 个数值到列表中,然后使用冒泡排序算法按升序排序它们。
解决方案
冒泡排序算法可能是效率最低的排序算法之一,但它广泛用于教学目的。主要思想(当要求按升序对列表进行排序时)是反复将列表中的最小元素移动到最低索引的位置。这个过程如下:算法遍历列表中的元素,比较每一对相邻元素,然后交换它们的内容(如果它们顺序错误)。这个过程会重复多次,直到列表被排序。
例如,让我们尝试按升序排序以下列表。
.
最低的值是 5。根据冒泡排序算法,这个值必须逐渐“冒泡”或“上升”到位置 0,就像可乐中的气泡上升一样。当值 5 被移动到位置 0 后,下一个最小的值是 8。现在,值 8 必须“冒泡”到位置 1。接下来是值 12,它必须“冒泡”到位置 2,以此类推。这个过程会重复进行,直到所有元素都放置到正确的位置。
但如何使用算法来实现这种“冒泡”呢?让我们更详细地看看整个过程。对于之前包含六个元素的列表 A,需要进行五次遍历。
第一次遍历
第 1 次比较
初始时,索引位置 4 和 5 的元素被比较。由于值 12 小于值 49,这两个元素交换了它们的内容。
第 2 次比较
索引位置 3 和 4 的元素被比较。由于值 12 不小于值 5,没有进行交换。
第 3 次比较
索引位置 2 和 3 的元素被比较。由于值 5 小于值 8,这两个元素交换了它们的内容。
第 4 次比较
索引位置 1 和 2 的元素被比较。由于值 5 小于值 25,这两个元素交换了它们的内容。
第 5 次比较
索引位置 0 和 1 的元素被比较。由于值 5 小于值 17,这两个元素交换了它们的内容。

第一次遍历已经完成,但正如你所见,列表还没有被排序。唯一可以保证放置在正确位置的值是 5。然而,由于还有更多的遍历,所以没有必要让值 5 参与后续的比较。在接下来的遍历中,将执行更少的比较——即四个比较。
第二次遍历
第 1 次比较
索引位置 4 和 5 的元素被比较。由于数值 49 不小于数值 12,没有进行交换。
第二次比较
索引位置 3 和 4 的元素被比较。由于数值 12 不小于数值 8,没有进行交换。
第三次比较
索引位置 2 和 3 的元素被比较。由于数值 8 小于数值 25,这两个元素交换了它们的内容。
第四次比较
索引位置 1 和 2 的元素被比较。由于数值 8 小于数值 17,这两个元素交换了它们的内容。

第二次遍历已完成,并且数值 8 已经确保被放置在正确的位置。然而,由于还有更多的遍历将跟随,数值 8(当然,还有 5)不需要参与后续的比较。在接下来的遍历中,将执行一个更少的比较——即三个比较。
第三次遍历
在这次遍历中,执行了三个比较(但只有两个交换),如下所示。

第三次遍历已完成,并且数值 12 已经确保被放置在正确的位置。正如之前所述,由于还有更多的遍历将跟随,数值 12(当然,还有数值 5 和 8)不需要参与后续的比较。在接下来的遍历中,将执行一个更少的比较——即两个比较。
第四次遍历
在这次遍历中,执行了两个比较(没有交换),如下所示。

第四次遍历已完成,并且数值 17 已经确保被放置在正确的位置。正如之前所述,由于还有最后一次遍历将跟随,数值 17(当然,还有数值 5、8 和 12)不需要参与后续的比较。在接下来的最后一次遍历中,将执行一个更少的比较——即一个比较。
第五次遍历
在这次最后一次遍历中,只执行了一个比较。由于数值 49 不小于数值 25,没有进行交换。
.
第五次遍历已完成,并且最终的两个数值(25 和 49)现在确保被放置在正确的位置。冒泡排序算法已完成,列表已按升序排序!
现在您需要一个可以完成整个先前过程的 Python 程序。让我们使用“从内到外”的方法。下面是仅执行第一次遍历的代码片段。请注意,这是一个内部(嵌套)循环控制结构。假设变量 m 包含值 0。
for n in range(ELEMENTS - 1, m, -1):
if a[n] < a[n - 1]:
temp = a[n]
a[n] = a[n - 1]
a[n - 1] = temp
在第一次遍历中,变量 m 必须包含值 0。这确保了在最后一次迭代中,被比较的元素是索引位置 1 和 0 的元素。
交换两个元素的内容使用你已经学过的方法!请回忆一下两个橙汁和柠檬汁的例子。如果这让你感到困惑,你需要刷新你的记忆并重新阅读练习 8.1-3。
请记住,你可以像这里所示的那样以更 Pythonic 的方式交换 a[n]和 a[n − 1]的内容。
for n in range(ELEMENTS - 1, m, -1):
if a[n] < a[n - 1]:
a[n], a[n - 1] = a[n - 1], a[n]
如果只是重新执行之前的代码片段,就可以进行第二次遍历。然而,变量 m 需要包含值 1,这将确保索引位置 0 的元素不会再次进行比较。同样,对于第三次遍历,可以重新执行之前的代码片段,但变量 m 需要包含值 2,原因相同。
因此,之前的代码片段需要执行五次(每次遍历一次),每次变量 m 必须增加 1。使用冒泡排序算法对列表 a 进行排序的最终代码片段如下。
for m in range(ELEMENTS - 1):
for n in range(ELEMENTS - 1, m, -1):
if a[n] < a[n - 1]:
a[n], a[n - 1] = a[n - 1], a[n]
对于 N 个元素,算法需要执行 N - 1 次遍历。例如,如果列表 a 包含 20 个元素,那么for m in range(ELEMENTS - 1)的语句将执行 19 次遍历。
完整的 Python 程序如下。
file_33.4-1
ELEMENTS = 20
a = [None] * ELEMENTS
for i in range(ELEMENTS):
a[i] = float(input())
for m in range(ELEMENTS - 1):
for n in range(ELEMENTS - 1, m, -1):
if a[n] < a[n - 1]:
a[n], a[n - 1] = a[n - 1], a[n]
for i in range(ELEMENTS):
print(a[i], end = "\t")
冒泡排序算法非常低效。它执行的总比较次数是
,其中 N 是列表元素的总数。
交换的总次数取决于给定的列表。最坏的情况是你想要对一个已经按降序排序的列表进行升序排序,或者相反。
练习 33.4-2 使用字母数字值对一维列表进行排序
编写一个代码片段,使用冒泡排序算法按降序对列表的字母数字值进行排序。
解答
将本练习的措辞与上一个练习进行比较,有两点不同。首先,冒泡排序算法需要排序字母数字值,例如人名或城市名;其次,它必须按降序排序。
为了对字母数字数据进行排序,你不需要在算法中做任何改变!Python 处理字母的方式与处理数字的方式相同。字母“A”被认为是“小于”字母“B”,“B”被认为是“小于”字母“C”,依此类推。当然,如果列表中包含以相同字母开头的单词,Python 会继续比较它们的第二个字母,也许还会比较第三个字母(如果需要的话)。例如,名字“Johathan”被认为是“小于”名字“Jone”,因为第四个字母“a”是“小于”第四个字母“e”。
考虑在英语词典中单词组织的情况下进行字母数字排序。
现在,让我们看看你需要如何修改算法,以便它能够按降序排序而不是升序。你还记得冒泡排序算法实际上是怎样工作的吗?元素逐渐“冒泡”到最低索引的位置,就像汽水中的气泡上升一样。在这个练习中,你想要的是让较大的(而不是较小的)元素“冒泡”到较低的索引位置。因此,你所需要做的就是简单地反转决策控制结构中的比较运算符!
排序字母数字值(当然也包括数值)的降序代码片段如下。
for m in range(ELEMENTS − 1)
for n in range(ELEMENTS - 1, m, -1):
if a[n] > a[n - 1]:
a[n], a[n - 1] = a[n - 1], a[n]
练习 33.4-3 在保持与第二个列表关系的同时对一维列表进行排序
编写一个 Python 程序,让用户输入 20 个湖泊及其相应的平均面积。程序必须然后使用冒泡排序算法按平均面积升序排序它们。
解答
在这个练习中,你需要以下两个列表。

如果你想要在保持两个列表元素一对一对应关系的同时对列表 areas 进行排序,你必须重新排列列表 names 的元素。这意味着每当列表 areas 中的两个元素交换内容时,对应的列表 names 中的元素也必须交换内容。以下是一个 Python 程序。
file_33.4-3
LAKES = 20
names = [None] * LAKES
areas = [None] * LAKES
for i in range(LAKES):
names[i] = input()
areas[i] = float(input())
for m in range(LAKES - 1):
for n in range(LAKES - 1, m, -1):
if areas[n] < areas[n - 1]:
areas[n], areas[n - 1] = areas[n - 1], areas[n]
names[n], names[n - 1] = names[n - 1], names[n]
for i in range(LAKES):
print(names[i], "\t", areas[i])
练习 33.4-4 对姓氏和名字进行排序
编写一个 Python 程序,提示用户输入 100 个人的姓氏和名字。程序必须然后按姓氏的字母顺序显示这些名字。在两个或更多人姓氏相同的情况下,他们的名字应该按字母顺序显示。
解答
您已经知道如何在保持与第二个列表元素一对一对应的同时对列表进行排序。现在,您必须处理第一个列表中两个姓氏相等的情况。根据练习的描述,第二个列表中对应的名字也必须按字母顺序排序。例如,以下 lastNm 列表包含 100 个人的姓氏。它按字母顺序排序,并包含姓氏“Parker”三次。相应的名字列表 firstNm 中的“Andrew”、“Anna”和“Chloe”也必须按字母顺序排序,如下所示。

为了您的方便,这里再次呈现冒泡排序算法的基本版本。请注意,此算法保留了列表 lastNm 和 firstNm 中元素的一对一对应关系。
for m in range(PEOPLE - 1):
for n in range(PEOPLE - 1, m, -1):
if lastNm[n] < lastNm[n - 1]:
lastNm[n], lastNm[n - 1] = lastNm[n - 1], lastNm[n]
firstNm[n], firstNm[n - 1] = firstNm[n - 1], firstNm[n]
然而,为了解决这个练习,这个冒泡排序算法必须相应地进行调整。根据冒泡排序算法的基本版本,当位置 n 的姓氏“小于”位置 n-1 的姓氏时,算法会交换相应的内容。但是,如果这两个位置的姓氏相等,则算法必须验证对应的名字是否处于正确的顺序。如果不正确,则需要在 firstNm 列表中进行交换。调整后的冒泡排序算法在以下代码片段中展示。
for m in range(PEOPLE - 1):
for n in range(PEOPLE - 1, m, -1):
if lastNm[n] < lastNm[n - 1]:
lastNm[n], lastNm[n - 1] = lastNm[n - 1], lastNm[n]
firstNm[n], firstNm[n - 1] = firstNm[n - 1], firstNm[n]
elif lastNm[n] == lastNm[n - 1]: # 如果姓氏相等
if firstNm[n] < firstNm[n - 1]: # 检查对应的名字
如果姓名顺序不正确,则交换第一个名字
firstNm[n], firstNm[n - 1] = firstNm[n - 1], firstNm[n]
下一个展示的是最终的 Python 程序。
file_33.4-4
PEOPLE = 100
读取 firstNm 和 lastNm 列表
firstNm = [None] * PEOPLE
lastNm = [None] * PEOPLE
for i in range(PEOPLE):
firstNm[i] = input("为第" + str(i + 1) + "个人输入名字:")
lastNm[i] = input("为第" + str(i + 1) + "个人输入姓氏:")
对 lastNm 和 firstNm 列表进行排序
for m in range(PEOPLE - 1):
for n in range(PEOPLE - 1, m, -1):
if lastNm[n] < lastNm[n - 1]:
lastNm[n], lastNm[n - 1] = lastNm[n - 1], lastNm[n]
firstNm[n], firstNm[n - 1] = firstNm[n - 1], firstNm[n]
elif lastNm[n] == lastNm[n - 1]:
if firstNm[n] < firstNm[n - 1]:
firstNm[n], firstNm[n - 1] = firstNm[n - 1], firstNm[n]
显示 lastNm 和 firstNm 列表
for i in range(PEOPLE):
print(lastNm[i], "\t", firstNm[i])
练习 33.4-5 对二维列表进行排序
编写一个代码片段,以升序对二维列表的每一列进行排序。
Solution
一个二维列表的例子如下。

由于这个列表有七列,冒泡排序算法需要执行七次,每次对应一列。因此,整个冒泡排序算法应该嵌套在一个循环中,该循环迭代七次。
但让我们把事情放在正确的顺序上。使用“从内到外”的方法,接下来的代码片段只对二维列表 a 的第一列(列索引 0)进行排序。假设变量 j 包含值 0。
for m in range(ROWS - 1):
for n in range(ROWS - 1, m, -1):
if a[n][j] < a[n - 1][j]:
a[n][j], a[n - 1][j] = a[n - 1][j], a[n][j]
现在,为了对所有列进行排序,你可以将这个代码片段嵌套在一个循环中,该循环遍历所有列,如下所示。
for j in range(COLUMNS):
for m in range(ROWS - 1):
for n in range(ROWS - 1, m, -1):
if a[n][j] < a[n - 1][j]:
a[n][j], a[n - 1][j] = a[n - 1][j], a[n][j]
这并不难,对吧?
练习 33.4-6 修改后的冒泡排序算法 – 对一维列表进行排序
编写一个 Python 程序,允许用户输入 20 个人的体重,然后显示三个最重的体重和三个最轻的体重。使用修改后的冒泡排序算法。
Solution
为了解决这个练习,Python 程序可以对用户提供的数据进行升序排序,然后显示索引位置 17、18 和 19(对应三个最重的重量)以及索引位置 0、1 和 2(对应三个最轻的重量)。但这个修改后的冒泡排序算法是什么,它实际上是如何工作的呢?假设你有一个包含六个人体重的列表。

如果你仔细观察,你可以自己确认,唯一不在正确位置上的元素是索引位置 3 和 4 的元素。如果你交换它们的值,列表 w 立即变得有序!不幸的是,冒泡排序算法并不是这样操作的。对于这个包含六个元素的列表,它将进行五次遍历,总共
次比较,其中 N 是列表元素的总数。对于更长的列表,冒泡排序算法执行的比较次数将以指数级增加!例如,对于一个包含 1000 个元素的列表,冒泡排序算法将执行 499,500 次比较!
当然,修改后的冒泡排序算法可以克服这种情况,如下所示:如果执行了完整的遍历且没有进行交换,则这表明列表现在已排序,无需进一步遍历。为了实现这一点,Python 程序可以使用一个标志变量来指示是否进行了交换。在遍历开始时,可以将 False 分配给标志变量;当进行交换时,将 True 分配。如果在遍历结束时标志仍然是 False,这表明没有进行交换,因此必须停止迭代。下面展示了修改后的冒泡排序。它使用 break 语句和 swaps 标志变量。
for m in range(ELEMENTS - 1):
将 False 分配给变量 swaps
swaps = False
执行新的遍历
for n in range(ELEMENTS - 1, m, -1):
if w[n] < w[n - 1]:
w[n], w[n - 1] = w[n - 1], w[n]
swaps = True
如果变量 swaps 仍然是 False,则在此遍历中没有进行交换。停止迭代!
if not swaps: break
每次新的遍历开始时,必须将 False 分配给 swaps 变量。这就是为什么必须在两个 for 语句之间放置 swaps = False 语句的原因。
语句 if not swaps 等价于语句 if swaps == False
以下展示了最终的 Python 程序。
file_33.4-6
ELEMENTS = 20
w = [None] * ELEMENTS
for i in range(ELEMENTS):
w[i] = float(input())
for m in range(ELEMENTS - 1):
swaps = False
for n in range(ELEMENTS - 1, m, -1):
if w[n] < w[n - 1]:
w[n], w[n - 1] = w[n - 1], w[n]
swaps = True
if not swaps: break
print("最重的三个重量是:")
print(w[-3], w[-2], w[-1])
print("最轻的三个重量是:")
print(w[0], w[1], w[2])
练习 33.4-7 选择排序算法 – 对一维列表进行排序
编写一个使用选择排序算法对列表元素进行升序排序的代码片段。
解决方案
选择排序算法对于大规模数据来说效率不高,冒泡排序算法也是如此,但它通常比后者表现更好。它是所有排序算法中最简单的,在有限主内存(RAM)的计算机系统中表现良好。
算法找到列表中最小(或最大,取决于排序顺序)的元素,并将其内容与位置 0 的元素交换。然后对列表的其余部分重复此过程;找到下一个最小(或最大)元素并将其放入下一个位置,直到检查所有元素。
例如,让我们尝试按升序排序以下列表。

最低值是位置 4 的值 4。根据选择排序算法,此元素将其内容与位置 0 的元素交换。列表 A 变为

列表剩余部分(索引位置 1 到 5)中的最低值是 9,位于位置 5。此元素将其内容与位置 1 的元素交换。列表 A 变为

列表剩余部分(索引位置 2 到 5)中的最低值是 18,位于位置 4。此元素将其内容与位置 2 的元素交换。列表 A 变为

按照同样的方式,下一个最低的值是 19,位于位置 5。列表 A 变为

下一个最低的值是 36,位于位置 5。此元素将其内容与位置 4 的元素交换,列表 A 最终按升序排序!

现在,让我们编写相应的 Python 程序。使用“从内到外”的方法,以便你更好地理解整个过程。下一个代码片段找到最小元素,然后将其内容与位置 0 的内容交换。请注意,这是一个内层(嵌套)循环控制结构。假设变量 m 包含值 0。
minimum = a[m]
indexOfMin = m
for n in range(m, ELEMENTS):
if a[n] < minimum:
minimum = a[n]
indexOfMin = n
找到最小值!现在,交换值。
a[m], a[indexOfMin] = a[indexOfMin], a[m]
现在,为了对所有列表元素重复这个过程,你可以在一个遍历所有元素的 for 循环中嵌套这个代码片段。按升序排序列表的选择排序算法如下。
for m in range(ELEMENTS):
minimum = a[m]
indexOfMin = m
for n in range(m, ELEMENTS):
if a[n] < minimum:
minimum = a[n]
indexOfMin = n
a[m], a[indexOfMin] = a[indexOfMin], a[m]
如果你希望按降序对列表进行排序,你只需要搜索最大值而不是最小值。
就像冒泡排序算法一样,在使用选择排序算法对字母数字数据进行排序时,你可以做一件简单的事情:保持算法原样!
练习 33.4-8 在保持与第二个列表关系的同时对一维列表进行排序
编写一个 Python 程序,提示用户输入一年内每月消耗的总千瓦时数。然后显示三个月最高消耗的千瓦时数,以及相应的千瓦时数(按降序排列)。使用选择排序算法。
解决方案
在这个练习中,你需要以下两个一维列表。

当选择排序算法对列表 kwh 的元素进行排序时,必须保持与列表 months 的元素的一对一对应关系。这意味着每次列表 kwh 的两个元素交换内容时,对应的列表 months 的元素也必须交换它们的内容。
然而,鉴于你只需要三个消耗 KWh 最高的月份,选择排序算法应该只排序前三个元素。Python 程序如下。
file_33.4-8
months = ["一月", "二月", "三月", "四月", "五月", "六月",
"七月", "八月", "九月", "十月", "十一月", "十二月"]
kwh = [None] * len(months)
for i in range(len(months)):
kwh[i] = float(input("Enter kWh for " + months[i] + ": "))
for m in range(3): #只排序前三个元素
maximum = kwh[m]
indexOfMax = m
for n in range(m, len(months)):
if kwh[n] > maximum:
maximum = kwh[n]
indexOfMax = n
交换 kwh 的值
kwh[m], kwh[indexOfMax] = kwh[indexOfMax], kwh[m]
交换月份的值
months[m], months[indexOfMax] = months[indexOfMax], months[m]
for i in range(3):
print(months[i], ":", kwh[i])
如果这个练习需要使用冒泡排序而不是选择排序算法,你可以使用相同的“技巧”。算法可以执行 3 次遍历而不是 ELEMENTS – 1 次遍历。
练习 33.4-9 插入排序算法 – 对一维列表进行排序
编写一个代码片段,使用插入排序算法按升序排序列表中的元素。
解决方案
插入排序算法对于大规模数据来说效率不高,选择排序和冒泡排序算法也是如此,但它通常比它们中的任何一个表现都要好。此外,当排序非常小的列表时,插入排序算法可以非常快——有时甚至比快速排序算法还要快。
插入排序算法类似于你可能用来排序扑克牌的方式。你开始时将所有扑克牌面朝下放在桌上。桌上的扑克牌代表未排序的“列表”。一开始你的左手是空的,但最后这只手将握有排序好的扑克牌。这个过程是这样的:你一次从桌上拿一张牌,并将其插入到左手正确的位置。为了找到一张牌的正确位置,你将其与手中已有的每张牌从右到左进行比较。最后,桌上必须没有牌,你的左手将握有所有已排序的牌。
例如,让我们尝试按升序排序以下列表。为了更好地理解这个例子,假设排序过程已经开始了,并且列表的前三个元素已经被排序。

索引位置 0、1 和 2 的元素代表你左手中的牌,而列表的其余元素代表桌上未排序的牌。
位置 3(即 8)的元素从列表中移除,并且其左侧所有大于 8 的元素都向右移动。列表 A 变为

现在有一个位置被释放了,值 8 被插入其中。列表变为

位置 4(值为 10)的元素从列表中移除,并且所有值大于 10 的左侧元素都向右移动。列表 A 变为

现在已经释放了一个位置,将 10 的值插入其中。列表变为

位置 5(值为 18)的元素从列表中移除,并且所有值大于 18 的左侧元素都向右移动。列表 A 变为

将 18 的值插入到释放的位置。列表变为

位置 6(值为 9)的元素从列表中移除,并且所有值大于 9 的左侧元素都向右移动。列表 A 变为

最后,将 9 的值插入到释放的位置,算法结束,列表现在已排序。

![img/notice.jpg]该算法实际上是对未排序的元素逐一进行检查,并将每个元素插入到已排序元素的正确位置。
使用插入排序算法按升序排序列表的代码片段如下。
for m in range(1, ELEMENTS):
"移除"列表中索引位置为 m 的元素并将其保存在变量 element 中
element = a[m]
将适当的元素向右移动
n = m
while n > 0 and a[n - 1] > element:
a[n] = a[n - 1]
n -= 1
将之前"移除"的元素插入到索引位置 n
a[n] = element
![img/notice.jpg]请注意,索引位置为 m 的元素实际上并没有从列表中移除,而是在向右移动时被覆盖。这就是为什么在移动元素之前需要将其值保存在变量 element 中的原因。
![img/notice.jpg]如果你希望按降序对列表进行排序,你只需要将 while 语句的布尔表达式更改为 n > 0 和 a[n - 1] < element。
![img/remember.jpg]正如前两个排序算法一样,为了对字母数字数据进行排序,你在这个算法中不需要做任何改变!
练习 33.4-10 三次最长的耗时
十位赛车手在赛道上尽可能快地驾驶他们的赛车。每辆车跑 20 圈,每圈对应的时间(以秒为单位)被记录下来。编写一个 Python 程序,提示用户输入每位驾驶员的名字以及他们每圈的时间。然后程序必须显示每位驾驶员的名字以及他们的三次最长时间。使用插入排序算法。
解答
在这个练习中,你需要以下两个列表。

用户输入所有数据后,Python 程序必须按降序对列表中的每一行进行排序,但最终必须只显示前三个列。
使用“从内到外”的方法,下一个代码片段使用插入排序算法对二维列表 elapsedTimes 的第一行(行索引 0)进行降序排序。假设变量 i 包含值 0。
for m in range(1, LAPS):
element = elapsedTimes[i][m]
n = m
while n > 0 and elapsedTimes[i][n - 1] < element:
elapsedTimes[i][n] = elapsedTimes[i][n - 1]
n -= 1
elapsedTimes[i][n] = element
现在,为了对所有行进行排序,你需要将此代码片段嵌套在一个 for 循环中,该循环遍历所有行,如下所示。
for i in range(CARS):
for m in range(1, LAPS):
element = elapsedTimes[i][m]
n = m
while n > 0 and elapsedTimes[i][n - 1] < element:
elapsedTimes[i][n] = elapsedTimes[i][n - 1]
n -= 1
elapsedTimes[i][n] = element
现在,让我们专注于给定的练习。最终的 Python 程序如下所示。
file_33.4-10
CARS = 10
LAPS = 20
一起读取名字和时间
names = [None] * CARS
elapsedTimes = [[None] * LAPS for i in range(CARS)]
for i in range(CARS):
names[i] = input("请输入第" + str(i + 1) + "号司机的名字:" + str(input())))
for j in range(LAPS):
elapsedTimes[i][j] = float(input("请输入第" + str(j + 1) + "圈的时间:" + str(float(input()))))
对列表 elapsedTimes 进行排序
for i in range(CARS):
for m in range(1, LAPS):
element = elapsedTimes[i][m]
n = m
while n > 0 and elapsedTimes[i][n - 1] < element:
elapsedTimes[i][n] = elapsedTimes[i][n - 1]
n -= 1
elapsedTimes[i][n] = element
显示 3 个最慢的时间
for i in range(CARS):
print("最慢的时间为", names[i])
print("-------------------------------------")
for j in range(3):
print(elapsedTimes[i][j])
33.5 在数据结构中搜索元素
在计算机科学中,搜索算法是一种在数据集中搜索具有特定特征的项的算法。在数据结构的情况下,搜索算法搜索数据结构以找到等于给定值的元素,或元素。
在数据结构中搜索时,可能会有两种情况。
►你想要在一个可能包含相同值多次的数据结构中搜索一个给定的值。因此,你需要找到所有等于该给定值的元素(或它们的对应索引)。
►你想要在一个每个值都是唯一的数据结构中搜索一个给定的值。因此,你需要找到只有一个元素(或其对应索引),即等于给定值的元素,然后停止进一步搜索!
最常用的搜索算法有:
►线性(或顺序)搜索算法
►二分搜索算法
线性和二分搜索算法都有优点和缺点。
练习 33.5-1 线性搜索算法 – 在可能包含相同值多次的一维列表中进行搜索
编写一个代码片段,对一维列表进行搜索,以找到用户提供的值。假设列表包含数值,并且可能包含多个相同的值。使用线性搜索算法。
解决方案
线性(或顺序)搜索算法检查列表的第一个元素是否等于给定值,然后检查第二个元素,然后是第三个,依此类推,直到列表的末尾。由于逐个检查元素的过程相当慢,线性搜索算法适用于元素较少的列表。
下面的代码片段展示了如何查找用户提供的值 needle 在列表 haystack 中的位置!
needle = float(input("输入要搜索的值:"))
found = False
for i in range(ELEMENTS):
if haystack[i] == needle:
print(needle, "在位置:", i, "找到")
found = True
if not found:
print("未找到任何内容!")
练习 33.5-2 显示具有相同姓氏的所有人的姓氏
编写一个 Python 程序,提示用户输入 20 个人的名字:将他们的名字分别输入到 firstNames 列表和 lastNames 列表中。然后程序必须询问用户输入一个名字,程序将搜索并显示所有名字与提供名字相同的人的姓氏。
解决方案
尽管练习的措辞并不明确,但确实列表 firstNames 可能包含多个相同的值。比如遇到两个名叫“John”的人有多罕见?
程序必须在列表 firstNames 中搜索用户提供的名字,并且每次找到它时,必须显示来自另一个列表的相应姓氏。
解决方案如下。
file_33.5-2
PEOPLE = 20
firstNames = [None] * PEOPLE
lastNames = [None] * PEOPLE
for i in range(PEOPLE):
firstNames[i] = input("输入名字:")
lastNames[i] = input("输入姓氏:")
获取要搜索的名字并将其转换为大写
needle = input("输入要搜索的名字:").upper()
在列表 firstNames 中搜索用户提供的值
found = False
for i in range(PEOPLE):
if firstNames[i].upper() == needle: # 转换为大写并比较
print(lastNames[i])
found = True
if not found:
print("没有找到任何人!")
由于程序处理的是字母数字数据,需要使用 upper() 方法,以确保程序可以正确地处理任何用户提供的值。例如,如果值“John”存在于列表 firstNames 中,而用户想要搜索的值是“JOHN”,upper() 方法确保程序找到所有的“John”。
练习 33.5-3 线性搜索算法 – 在可能包含多个相同值的二维列表中进行搜索
编写一个代码片段,对二维列表的每一行进行搜索,以找到用户提供的值。假设列表包含数值,并且可能包含多个相同的值。使用线性搜索算法。
解决方案
此代码片段必须在可能包含相同值多次出现的二维列表的每一行中搜索用户提供的数字。这意味着代码片段必须在第一行中搜索用户提供的数字,并显示找到该数字的所有列;否则,它必须显示一条消息,说明用户提供的数字在第一行中没有找到。然后,它必须在第二行中搜索,这个过程必须继续,直到检查完所有行。
为了更好地理解这个练习,使用了“由内到外”的方法。下面的代码片段仅搜索名为 haystack 的两个维列表的第一行中的给定值(变量 needle)。假设变量 i 包含值 0。
found = False
for j in range(COLUMNS):
if haystack[i][j] == needle:
print("在列", j, "找到")
found = True
if not found:
print("在第", i, "行未找到任何内容")
现在,为了在所有行中进行搜索,你需要将此代码片段嵌套在一个遍历所有行的 for 循环中,如下所示。
needle = float(input("输入要搜索的值:"))
for i in range(ROWS):
found = False
for j in range(COLUMNS):
if haystack[i][j] == needle:
print("在列", j, "找到")
found = True
if not found:
print("在第", i, "行未找到任何内容")
练习 33.5-4 线性搜索算法 - 在包含唯一值的单维列表中进行搜索
编写一个代码片段,在单维列表中执行搜索以找到用户提供的值。假设列表包含数值,并且列表中的每个值都是唯一的。使用线性搜索算法。
解决方案
这种情况与前面的情况有很大不同。由于列表中的每个值都是唯一的,当找到用户提供的值时,就没有必要无谓地迭代到列表的末尾,从而浪费 CPU 时间。实际上有三种方法!让我们分析一下所有的方法!
第一种方法 - 使用 break 语句
在这种方法中,当找到用户提供的值时,使用 break 语句跳出 for 循环。解决方案如下。
needle = float(input("输入要搜索的值:"))
found = False
for i in range(ELEMENTS):
if haystack[i] == needle:
print(needle, "在位置", i, "找到")
found = True
break
if not found:
print("未找到任何内容!")
或者,你也可以以稍微不同的方式做同样的事情。
needle = float(input("输入要搜索的值:"))
indexPosition = -1
for i in range(ELEMENTS):
if haystack[i] == needle:
indexPosition = i
break
if indexPosition == -1:
print("未找到任何内容!")
else:
print(needle, "在位置", indexPosition, "找到")
第二种方法 - 使用标志
break 语句并不是所有计算机语言都有的;由于本书的目的是教你“算法思维”(而不仅仅是 Python 支持的特殊语句),让我们看看另一种方法。
在下一个代码片段中,当用户提供的值在列表 haystack 中找到时,变量 found 强制执行流程立即退出循环。
needle = float(input("Enter a value to search: "))
found = False
i = 0
while i < ELEMENTS and not found:
if haystack[i] == needle:
found = True
indexPosition = i
i += 1
if not found:
print("Nothing found!")
else:
print(needle, "found at position:", indexPosition)
第三种方法 – 仅使用预测试循环结构
这种方法可能是三种方法中最有效率的。while 循环遍历列表,将每个元素与针值进行比较。只要满足两个条件,循环就会继续:变量 i(表示索引)在列表 haystack 的有效范围内,并且列表当前索引处的值不等于针值。如果两个条件都为真,变量 i 就会增加以移动到下一个元素。这个过程会一直持续到找到匹配项或到达列表的末尾。
needle = float(input("Enter a value to search: "))
i = 0
while i < ELEMENTS - 1 and haystack[i] != needle:
i += 1
if haystack[i] != needle:
print("Nothing found!")
else:
print(needle, "found at position:", i)
练习 33.5-5 搜索社会保障号码
在美国,社会保障号码(SSN)是一个九位身份号码,用于识别所有美国公民以进行社会保障目的。编写一个 Python 程序,提示用户输入 100 人的 SSN 和姓名。然后程序必须要求用户输入一个 SSN,程序将搜索并显示持有该 SSN 的人的姓名和姓氏。
解决方案
在美国,两个人或更多人拥有相同 SSN 的可能性为零。因此,尽管练习的措辞并不明确,但持有 SSN 的列表中的每个值都是唯一的!
根据你迄今为止所学的一切,本练习的解决方案如下。
file_33.5-5
PEOPLE = 100
SSNs = [None] * PEOPLE
firstNames = [None] * PEOPLE
lastNames = [None] * PEOPLE
for i in range(PEOPLE):
SSNs[i] = input("Enter SSN: ")
firstNames[i] = input("Enter first name: ")
lastNames[i] = input("Enter last name: ")
needle = input("Enter an SSN to search: ")
在列表 SSNs 中搜索用户提供的值
i = 0
while i < PEOPLE - 1 and SSNs[i] != needle:
i += 1
if SSNs[i] != needle:
print("Nothing found!")
else:
print(firstNames[i], lastNames[i])
练习 33.5-6 线性搜索算法 – 在包含唯一值的二维列表中进行搜索
一家民意调查公司在 10 个城市中进行电话调查,询问每个城市的 30 位公民是否锻炼。编写一个 Python 程序,提示用户输入每位公民的电话号码和他们的答案(Y 代表是,N 代表否,S 代表有时)。然后程序提示用户输入一个电话号码,并搜索并显示在此电话号码下提供的答案。程序还必须验证数据输入,并只接受 Y、N 或 S 作为答案。
解决方案
在这个练习中,你需要以下两个列表。

尽管练习的措辞并不明确,但列表 phoneNum 中的每个值都是唯一的!程序必须搜索用户提供的号码,如果找到,则停止搜索。解决方案如下。
file_33.5-6
CITIES = 10
CITIZENS = 30
phoneNum = [[None] * CITIZENS for i in range(CITIES)]
ans = [[None] * CITIZENS for i in range(CITIES)]
for i in range(CITIES):
print("城市编号", i + 1)
for j in range(CITIZENS):
phoneNum[i][j] = input("请输入第" + str(j + 1) + "号公民的电话号码:")
ans[i][j] = input("请输入第" + str(j + 1) + "号公民的答案:").upper()
while ans[i][j] not in ["Y", "N", "S"]:
ans[i][j] = input("回答错误。请输入有效的答案:").upper()
needle = input("输入要搜索的电话号码:")
found = False
for i in range(CITIES):
for j in range(CITIZENS):
if phoneNum[i][j] == needle: #如果找到了
found = True
positionI = i #保留找到针的位置的行索引
positionJ = j #保留找到针的列索引
break #退出内循环
if found:
break #如果找到了,也退出外循环
if not found:
print("电话号码未找到!")
else:
print("电话号码", phoneNum[positionI][positionJ], "给出了 '", end = "")
if ans[positionI][positionJ] == "Y":
print("Yes", end = "")
elif ans[positionI][positionJ] == "N":
print("No", end = "")
else:
print("有时", end = "")
print("' 作为答案")
练习 33.5-7 检查值是否存在于所有列中
编写一个 Python 程序,允许用户将数值输入到一个 20 × 30 的列表中。在所有数值都输入完毕后,程序接着让用户输入一个值。最后,如果用户提供的值至少在每个列表列中出现过一次,则显示一条消息。
解决方案
此练习可以使用线性搜索算法和计数变量 count 来解决。Python 程序将遍历第一列;如果找到用户提供的值,Python 程序必须在之后停止在第一列中搜索,并将变量 count 增加一。然后,程序将遍历第二列;如果再次找到用户提供的值,Python 程序必须在之后停止在第二列中搜索,并将变量 count 再次增加一。这个过程必须重复,直到检查完所有列。在过程结束时,如果 count 的值等于列的总数,这意味着用户提供的值至少在每个列表的列中存在一次。
让我们使用“从内到外”的方法。以下代码片段在列表的第一列(列索引 0)中搜索,如果找到用户提供的值,执行流程将退出 for 循环,变量 count 增加一。假设变量 j 包含值 0。
found = False
for i in range(ROWS):
if haystack[i][j] == needle:
found = True
break
if found:
count += 1
现在您可以在一个遍历所有列的 for 循环中嵌套此代码片段。
for j in range(COLUMNS):
found = False
for i in range(ROWS):
if haystack[i][j] == needle:
found = True
break
if found:
count += 1
您几乎准备好了——但考虑一下一个小细节!如果内层 for 循环在某一列中没有找到用户提供的值,外层 for 循环必须停止迭代。继续迭代是没有意义的,因为用户提供的值至少在一列中不存在。因此,更好的方法是在代码片段中显示使用 break 语句来对外层循环进行中断。
for j in range(COLUMNS):
found = False
for i in range(ROWS):
if haystack[i][j] == needle:
found = True
break
if found:
count += 1
else:
break
最终的 Python 程序如下。
file_33.5-7
ROWS = 20
COLUMNS = 30
haystack = [[None] * COLUMNS for i in range(ROWS)]
for i in range(ROWS):
for j in range(COLUMNS):
haystack[i][j] = float(input())
needle = float(input("请输入要搜索的值:"))
count = 0
for j in range(COLUMNS):
found = False
for i in range(ROWS):
if haystack[i][j] == needle:
found = True
break
if found:
count += 1
else:
break
if count == COLUMNS:
print(needle, "在每一列中找到!")
如果您需要在每一行(而不是每一列)至少存在一次用户提供的值时显示一条消息,Python 程序可以遵循之前显示的程序,但在此情况下,它必须遍历行而不是列。
练习 33.5-8 二分查找算法 – 在有序一维列表中进行搜索
编写一个代码片段,在有序一维列表中执行搜索以找到给定值。使用二分查找算法。
解决方案
二分查找算法被认为非常快,并且可以用于大规模数据。然而,其主要缺点是数据需要排序。
二分查找算法的主要思想是首先检查列表中间的元素。如果它不匹配你正在寻找的“针”,算法将确定目标是否小于或大于中间元素。这引导搜索到相应的列表一半。换句话说,如果你正在寻找的“针”小于中间元素的值,这意味着“针”可能在列表的前半部分;否则它可能在后半部分。这个过程继续,通过检查剩余一半列表中的中间元素来缩小搜索范围,直到找到“针”或正在检查的列表部分减少到单个元素。如果后者发生而没有找到“针”,这意味着“针”不在列表中。
感到困惑?让我们通过一个例子来尝试分析二分查找算法。以下列表包含按升序排列的数值。假设你正在寻找的“针”是值 44。

使用了三个变量。最初,变量 left 包含值 0(这是第一个元素的索引),变量 right 包含值 13(这是最后一个元素的索引),变量 middle 包含值 6(这是大约中间元素的索引)。

你正在寻找的“针”(值 44)比中间的值 39 大,因此你正在寻找的元素可能位于列表的后半部分。因此,变量 left 更新为指向索引位置 7,变量 middle 更新为指向 left(新的)和 right 之间的中间位置,如下所示。

现在,你正在寻找的“针”(值 44)比中间的值 57 小,因此你正在寻找的元素可能位于正在检查的列表部分的前半部分。因此,变量 right 现在更新为指向索引位置 9,变量 middle 更新为指向 left 和 right(新的)之间的中间位置,如下所示 002E

你完成了!“针”在索引位置 8 被找到,整个过程可以停止!
每次不成功的比较都会将需要检查的元素数量减半!
索引变量 left 和 right 每次分别指向正在检查的列表部分的开始和结束。
现在,让我们看看相应的代码片段。
left = 0
right = ELEMENTS - 1
found = False
当 left <= right 且未找到时:
middle = (left + right) // 2 # 这是一个除以 2 的操作
if needle < haystack[middle]: #如果针在部分的前半部分,
right = middle – 1 #正在检查的列表,更新右索引
elif needle > haystack[middle]: #如果它在后半部分,
left = middle + 1 #更新左索引
else:
found = True
if not found:
print("Nothing found!")
else:
print(needle, "found at position:", middle)
二分查找算法非常高效,因为它在每次迭代中极大地减少了搜索空间,使其对排序列表非常有效。使用此算法在示例列表中,值 44 可以在仅三次迭代内找到。相比之下,线性搜索算法需要九次迭代来完成相同的数据!
如果列表包含多个相同的值,二分查找算法只能找到其中一个。
练习 33.5-9 显示一个国家的所有历史事件
编写一个 Python 程序,提示用户按字母顺序输入 10 个国家的名称和每个国家 20 个重要历史事件(每个事件的简要描述)。然后 Python 程序必须提示用户输入一个国家,并将搜索并显示该国的所有事件。使用二分查找算法。
解决方案
在这个练习中,需要以下两个列表。

假设用户输入一个要搜索的国家,二分查找算法发现该国家位于列表 countryNames 的索引位置 2。然后程序可以使用这个 2 作为列表 eventDescriptions 的列索引,并显示第 2 列的所有事件描述。
The Python program is as follows.
file_33.5-9
EVENTS = 20
COUNTRIES = 10
countryNames = [None] * COUNTRIES
eventDescriptions = [[None] * COUNTRIES for i in range(EVENTS)]
for j in range(COUNTRIES):
countryNames[j] = input("Enter Country No" + str(j + 1) + ": ")
for i in range(EVENTS):
eventDescriptions[i][j] = input("Enter description for event No" + str(i + 1) + ": ")
needle = input("Enter a country to search: ").upper()
国家名称按字母顺序输入。
使用二分查找算法来搜索针
left = 0
right = EVENTS - 1
found = False
while left <= right and not found:
middle = (left + right) // 2
if needle < countryNames[middle].upper():
right = middle - 1
elif needle > countryNames[middle].upper():
left = middle + 1
else:
found = True
if not found:
print("No country found!")
else:
for i in range(EVENTS):
print(eventDescriptions[i][middle])
练习 33.5-10 在二维列表的每一列中搜索
编写一个 Python 程序,提示用户输入 10 个国家的名称以及每个国家 20 个重要历史事件(每个事件的简要描述)以及每个事件的对应年份。Python 程序必须然后提示用户输入一个年份,并将搜索并显示每个国家在该年发生的事件。使用二分查找算法。假设每个国家每年只有一个事件,并且用户按年份升序输入事件。
Solution
在这个练习中,需要以下三个列表。

为了编写对列表 eventYears 的每一列进行搜索的代码片段,让我们使用“从内到外”的方法。接下来的二分查找算法在第一列(列索引 0)中搜索用户提供的年份。假设变量 j 包含值 0。由于搜索是垂直进行的,并且为了提高程序的可读性,二分查找算法的左右变量已被重命名为 top 和 bottom。
top = 0
bottom = EVENTS - 1
found = False
while top <= bottom and not found:
middle = (top + bottom) // 2
if needle < eventYears[middle][j]:
bottom = middle - 1
elif needle > eventYears[middle][j]:
top = middle + 1
else:
found = True
if not found:
print("No event found for country", countryNames[j])
else:
print("Country:", countryNames[j])
print("Year:", eventYears[middle][j])
print("Event:", eventDescriptions[middle][j])
现在,将此代码片段嵌套在一个 for 循环中,该循环遍历所有列,结果如下。
for j in range(COUNTRIES):
top = 0
bottom = EVENTS - 1
found = False
while top <= bottom and not found:
middle = (top + bottom) // 2
if needle < eventYears[middle][j]:
bottom = middle - 1
elif needle > eventYears[middle][j]:
top = middle + 1
else:
found = True
if not found:
print("No event found for country", countryNames[j])
else:
print("Country:", countryNames[j])
print("Year:", eventYears[middle][j])
print("Event:", eventDescriptions[middle][j])
最终的 Python 程序如下。
file_33.5-10
EVENTS = 20
COUNTRIES = 10
countryNames = [None] * COUNTRIES
eventDescriptions = [[None] * COUNTRIES for i in range(EVENTS)]
eventYears = [[None] * COUNTRIES for i in range(EVENTS)]
for j in range(COUNTRIES):
countryNames[j] = input("Enter Country No." + str(j + 1) + ": ")
for i in range(EVENTS):
eventDescriptions[i][j] = input("Enter description for event No" + str(i + 1) + ": ")
eventYears[i][j] = int(input("Enter year for event No" + str(i + 1) + ": "))
needle = int(input("Enter a year to search: "))
for j in range(COUNTRIES):
top = 0
bottom = EVENTS - 1
found = False
while top <= bottom and not found:
middle = (top + bottom) // 2
if needle < eventYears[middle][j]:
bottom = middle - 1
elif needle > eventYears[middle][j]:
top = middle + 1
else:
found = True
if not found:
print("在以下国家没有找到事件", countryNames[j])
else:
print("国家:", countryNames[j])
print("年份:", eventYears[middle][j])
print("事件:", eventDescriptions[middle][j])
33.6 具有数据结构的通用练习
练习 33.6-1 在哪些天有可能下雪?
编写一个 Python 程序,让用户输入 1 月份每天同一小时记录的温度(华氏度)。然后,Python 程序必须显示那些有可能下雪的日子(1,2,...,31),即温度低于 36 华氏度(约 2 摄氏度)的日子。
解决方案
这个练习的一维列表如下。

Python 程序如下。
file_33.6-1
DAYS = 31
t = [None] * DAYS
for i in range(DAYS):
t[i] = int(input())
for i in range(DAYS):
if t[i] < 36:
print(i + 1, end = "\t")
练习 33.6-2 是否有可能下雪?
编写一个 Python 程序,让用户输入 1 月份每天同一小时记录的温度(华氏度)。然后,Python 程序必须显示是否有下雪的可能,即是否有温度低于 36 华氏度(约 2 摄氏度)。
解决方案
在这个练习中,你不能复制前一个练习中使用的方法。下面的代码片段将是错误的。
for i in range(DAYS):
if t[i] < 36:
print("一月份有可能下雪!")
如果一月份有不止一天的温度低于 36 华氏度,同样的信息会被多次显示——显然你不想这样!你实际上只想显示一次信息,不管一月份有没有一天、两天,甚至更多天的温度低于 36 华氏度。
实际上,有两种方法。让我们研究一下这两种方法。
第一种方法 - 计算所有低于 36 华氏度的温度
在这种方法中,你可以使用程序中的一个变量来计算所有温度低于 36 华氏度的日子。在检查完所有这些日子之后,程序可以检查这个变量的值。如果这个值不是零,这意味着至少有一天有可能下雪。
file_33.6-2a
DAYS = 31
t = [None] * DAYS
for i in range(DAYS):
t[i] = int(input())
count = 0
for i in range(DAYS):
if t[i] < 36:
count += 1
if count != 0:
print("一月份有可能下雪!")
第二种方法 - 使用标志
在这种方法中,你不需要计算所有温度低于 36 华氏度的日子,你可以使用一个布尔变量(标志)。解决方案将在下面展示。
file_33.6-2b
DAYS = 31
t = [None] * DAYS
for i in range(DAYS):
t[i] = int(input())
found = False
for i in range(DAYS):
if t[i] < 36:
found = True
break
if found:
print("一月份有可能下雪!")
想象一下找到的变量就像一个真正的旗帜。最初,旗帜没有升起(found = False)。然而,在 for 循环中,当找到低于 36 华氏度的温度时,旗帜升起(将 True 的值赋给变量 found),并且它永远不会再次降下。
注意 break 语句!一旦找到低于 36 华氏度的温度,继续检查就变得没有意义了。
如果循环完成了所有的迭代并且没有找到低于 36 华氏度的温度,变量 found 仍然包含其初始值(False),因为执行流程从未进入决策控制结构。
练习 33.6-3 在哪些城市有可能下雪?
编写一个 Python 程序,提示用户输入十个城市的名称和它们在一月份 31 天中每天同一小时记录的温度(华氏度)。Python 程序必须显示那些有可能下雪的城市名称,即温度低于 36 华氏度(约 2 摄氏度)的城市。
解决方案
与上一个练习一样,你需要显示每个城市的名称一次,无论它是否有一天、两天,甚至更多天的温度低于 36 华氏度。有两种方法。在第一种方法中,程序创建了一个辅助列表 count,如下所示,以统计每个城市温度低于 36 华氏度的总天数。然而,第二种方法不创建辅助列表 count。它只使用一个额外的布尔变量(一个旗帜)。显然,第二个更高效。但让我们研究一下两种方法。

第一种方法 – 使用辅助列表
你在 第 32.2 节 学到了如何逐行处理。可以创建辅助列表 count 的嵌套循环控制结构如下。
count = [None] * CITIES
for i in range(CITIES):
count[i] = 0
for j in range(DAYS):
if t[i][j] < 36:
count[i] += 1
在创建列表 count 之后,你可以遍历它,当元素包含的值不是零时,这意味着相应的城市至少有一天温度低于 36 华氏度;因此程序必须显示该城市的名称。接下来的 Python 程序如下所示
file_33.6-3a
CITIES = 10
DAYS = 31
names = [None] * CITIES
t = [[None] * DAYS for i in range(CITIES)]
for i in range(CITIES):
names[i] = input("输入第 " + str(i + 1) + " 个城市的名称: ")
for j in range(DAYS):
t[i][j] = int(input("输入第 " + str(j + 1) + " 天的温度: "))
创建辅助列表 count
count = [None] * CITIES
for i in range(CITIES):
count[i] = 0
for j in range(DAYS):
如果 t[i][j] < 36:
count[i] += 1
打印("一月份可能下雪的城市:")
for i in range(CITIES):
if count[i] != 0:
print(names[i])
第二种方法 - 使用标志
这种方法不使用辅助列表。它直接处理列表 t,并显示任何温度低于 36 华氏度的城市名称。但是,如何在不显示城市名称两次或更多次的情况下做到这一点?这就是你需要一个标志,即一个额外的布尔变量。
为了更好地理解这种方法,让我们使用“从内到外”的方法。以下代码片段检查列表 t 的第一行(行索引 0)是否至少包含一个低于 36 华氏度的温度;如果是这样,它将显示位于列表 names 位置 0 的相应城市名称。假设变量 i 的值为 0。
found = False
for j in range(DAYS):
if t[i][j] < 36:
found = True
break
if found:
print(names[i])
现在一切都已明确,为了处理整个列表 t,你只需将此代码片段嵌套在一个遍历所有城市的 for 循环中,如下所示。
for i in range(CITIES):
found = False
for j in range(DAYS):
if t[i][j] < 36:
found = True
break
if found:
print(names[i])
最终的 Python 程序如下。
file_33.6-3b
CITIES = 10
DAYS = 31
names = [None] * CITIES
t = [[None] * DAYS for i in range(CITIES)]
for i in range(CITIES):
names[i] = input("为城市编号:" + str(i + 1) + " 输入一个名称:")
for j in range(DAYS):
t[i][j] = int(input("为第" + str(j + 1) + "天输入温度:"))
打印("一月份可能下雪的城市:")
for i in range(CITIES):
found = False
for j in range(DAYS):
if t[i][j] < 36:
found = True
break
if found:
print(names[i])
练习 33.6-4 按学生平均成绩从高到低显示,并按字母顺序排列
有 10 名学生,他们每个人都已经完成了五节课的评分。编写一个 Python 程序,提示教师输入每个学生的姓名和所有课程的评分。然后程序必须计算每个学生的平均成绩,并按学生的平均成绩降序显示学生的姓名和平均成绩。此外,如果有两个或更多学生的平均成绩相同,他们的姓名必须按字母顺序显示。使用适当的冒泡排序算法。
解答
在这个练习中,你需要以下三个列表。列表 names 和 grades 的值将由用户输入,而辅助列表 average 将由 Python 程序创建。

你已经熟悉这个练习的所有步骤。你可以创建辅助列表 average(见第 32.2 节),在保持与列表 names 中元素一对一对应的同时对其进行排序(如练习 33.4-3 所示),并处理以下场景:如果两个平均成绩相等,则相应的学生姓名应按字母顺序排序(如练习 33.4-4 所示)。以下是最终的 Python 程序。
file_33.6-4
STUDENTS = 10
LESSONS = 5
读取列表 names 和 grades
names = [None] * STUDENTS
grades = [[None] * LESSONS for i in range(STUDENTS)]
for i in range(STUDENTS):
names[i] = input("Enter name for student No." + str(i + 1) + ": ")
for j in range(LESSONS):
grades[i][j] = int(input("Enter grade for lesson No." + str(j + 1) + ": "))
创建列表 average
average = [None] * STUDENTS
for i in range(STUDENTS):
average[i] = 0
for j in range(LESSONS):
average[i] += grades[i][j]
average[i] /= LESSONS
对列表 average 和 names 进行排序
for m in range(STUDENTS - 1):
for n in range(STUDENTS - 1, m, -1):
if average[n] > average[n - 1]:
average[n], average[n - 1] = average[n - 1], average[n]
names[n], names[n - 1] = names[n - 1], names[n]
elif average[n] == average[n - 1]:
if names[n] < names[n - 1]:
names[n], names[n - 1] = names[n - 1], names[n]
显示列表 names 和 average
for i in range(STUDENTS):
print(names[i], "\t", average[i])
练习 33.6-5 夏季奥运会的射箭比赛
在夏季奥运会的射箭比赛中,20 名运动员每人射六支箭。编写一个 Python 程序,提示用户输入每位运动员的姓名和每箭的得分。然后,程序必须显示获得金牌、银牌和铜牌的三位运动员的姓名,取决于哪位运动员获得了最高总分。假设没有两位运动员的总分相等。
解决方案
在这个练习中,你需要以下三个列表。列表 names 和 points 的值将由用户输入,而辅助列表 total 将由 Python 程序创建。

在创建辅助列表 total 之后,排序算法可以按降序对列表 total 进行排序(同时保持与列表 names 中元素的对应关系)。然后,Python 程序可以显示索引位置为 0、1 和 2 的运动员姓名(因为这些运动员应该分别获得金牌、银牌和铜牌)。
The following program uses the bubble sort algorithm to sort the list total. Since the algorithm must sort in descending order, bigger elements must gradually “bubble” to positions of lowest index, like bubbles rise in a glass of cola. However, instead of performing 19 passes (there are 20 athletes), given that only the three best athletes must be found, the algorithm can perform just 3 passes. Doing this, only the first three bigger elements will gradually “bubble” to the first three positions in the list.
The solution is presented next.
file_33.6-5
from math import fsum
ATHLETES = 20
SHOTS = 6
Read list names and points
names = [None] * ATHLETES
points = [[None] * SHOTS for i in range(ATHLETES)]
for i in range(ATHLETES):
names[i] = input("Enter name for athlete No." + str(i + 1) + ": ")
for j in range(SHOTS):
points[i][j] = int(input("Enter points for shot No." + str(j + 1) + ": "))
Create list total
total = []
for row in points:
total.append(fsum(row))
Sort lists names and total. Perform only 3 passes
for m in range(3):
for n in range(ATHLETES - 1, m, -1):
if total[n] > total[n - 1]:
total[n], total[n - 1] = total[n - 1], total[n]
names[n], names[n - 1] = names[n - 1], names[n]
Display gold, silver and bronze metal
for i in range(3):
print(names[i], "\t", total[i])
练习 33.6-6 五大最佳射手
Write a Python program that prompts the user to enter the names of the 32 national teams of the FIFA World Cup, the names of the 24 players for each team, and the total number of goals each player scored. The program must then display the name of each team along with its five best scorers. Use the bubble sort algorithm.
Solution
In this exercise you need the following three lists.

To save paper short list names are used, but it is more or less obvious that list t holds the names of the 32 national teams, list p holds the names of the 24 players of each team, and list g holds the total number of goals each player scored.
The Python program must sort each row of list g in descending order but it must also take care to preserve the one-to-one correspondence with the elements of list p. This means that, every time the bubble sort algorithm swaps the contents of two elements of list g, the corresponding elements of list p must be swapped as well. However, instead of performing 23 passes (there are 24 players), given that only the five best scorers must be found, the algorithm can perform just 5 passes. When sorting is completed, the five best scorers should appear in the first five columns.
再次使用“从内到外”的方法。以下代码片段对列表 g 的第一行(行索引 0)按降序排序,同时注意保持与列表 p 中元素的对应关系。假设变量 i 包含值 0。
for m in range(5): #Perform 5 passes
for n in range(PLAYERS - 1, m, -1):
if g[i][n] > g[i][n - 1]:
g[i][n], g[i][n - 1] = g[i][n - 1], g[i][n]
p[i][n], p[i][n - 1] = p[i][n - 1], p[i][n]
现在为了对所有行进行排序,你需要将此代码片段嵌套在一个遍历所有行的 for 循环中,如下所示。
for i in range(TEAMS):
for m in range(5): #执行 5 次遍历
for n in range(PLAYERS - 1, m, -1):
if g[i][n] > g[i][n - 1]:
g[i][n], g[i][n - 1] = g[i][n - 1], g[i][n]
p[i][n], p[i][n - 1] = p[i][n - 1], p[i][n]
最终的 Python 程序如下。
file_33.6-6
TEAMS = 32
PLAYERS = 24
一次性读取队伍名称、球员名称和进球数
t = [None] * TEAMS
p = [[None] * PLAYERS for i in range(TEAMS)]
g = [[None] * PLAYERS for i in range(TEAMS)]
for i in range(TEAMS):
t[i] = input("输入队伍 No." + str(i + 1) + "的名称: ")
for j in range(PLAYERS):
p[i][j] = input("输入球员 No." + str(j + 1) + "的名称: ")
g[i][j] = int(input("输入球员 No." + str(j + 1) + "的进球数: "))
对列表 g 进行排序
for i in range(TEAMS):
for m in range(5): #执行 5 次遍历
for n in range(PLAYERS - 1, m, -1):
if g[i][n] > g[i][n - 1]:
g[i][n], g[i][n - 1] = g[i][n - 1], g[i][n]
p[i][n], p[i][n - 1] = p[i][n - 1], p[i][n]
显示每个队伍的前 5 名得分手
for i in range(TEAMS):
print("队伍", t[i], "的最佳得分手")
print("----------------------------------")
for j in range(5):
print(p[i][j], "得分", g[i][j], "球")
练习 33.6-7 计算元音频率
编写一个 Python 程序,提示用户输入一个英文句子,并计算句子中每个元音的频率。使用字典来存储元音作为键和它们的频率作为值。
解决方案
在编程领域,文本数据的操作和分析起着至关重要的作用。一个常见的任务是在给定的文本中计数特定元素的频率,从而提供对其语言特征的洞察。元音是英语语言的基本组成部分,分析它们的频率可以揭示模式,有助于语言处理,甚至有助于某些加密算法。
在下面的解决方案中,程序首先创建一个名为 vowelsFrequency 的字典来存储和管理每个元音(A、E、I、O、U)的频率,初始频率都设置为 0。对于用户提供的句子中的每个字符,程序检查它是否是元音,如果是,则更新字典中相应的频率计数。
file_33.6-7
创建一个字典来存储每个元音的频率,初始频率都设置为 0。
频率都设置为 0。
vowelsFrequency = {"A": 0, "E": 0, "I": 0, "O": 0, "U": 0}
sentence = input("输入一个英文句子: ")
遍历用户提供的句子中的字符,如果它是元音,
在 vowelsFrequency 字典中更新(增加)相应的频率计数。
for character in sentence.upper():
if character in vowelsFrequency:
vowelsFrequency[character] += 1
显示每个元音的频率
for vowel in vowelsFrequency:
print(vowel + ":", vowelsFrequency[vowel])
33.7 复习问题:正确/错误
对以下每个陈述选择正确或错误。
1)冒泡排序算法(当按升序排序列表时)的主要思想是反复将列表中最小的元素移动到最低索引位置。
2)在一个升序排序的列表中,第一个元素是所有元素中最大的。
3)在使用冒泡排序算法时,总的交换次数取决于给定的列表。
4)冒泡排序算法执行最大次数交换的情况是在你想要对已经按升序排序的列表进行降序排序时。
5)在冒泡排序算法中,当决策控制结构测试布尔表达式 A[n] > A[n − 1]时,这意味着列表 A 的元素正在按降序排序。
6)在 Python 中,排序算法比较字母的方式与它们比较数字的方式相同。
7)如果你想对列表 A 进行排序但保留与列表 B 元素的逐一对应关系,你必须重新排列列表 B 的元素。
8)冒泡排序算法有时比改进的冒泡排序算法表现更好。
9)根据冒泡排序算法,在每次遍历(除了最后一次)中,只有一个元素保证被放置在正确的位置。
10)冒泡排序算法只能通过使用 for 循环来实现。
11)快速排序算法不能用于对二维列表的每一列进行排序。
12)插入排序算法可以按升序或降序排序。
13)修改的冒泡排序算法是速度最快的一种排序算法之一。
14)对于包含 N 个元素的线性列表,冒泡排序算法进行
次比较。
15)对于包含 N 个元素的线性列表,冒泡排序算法进行
次遍历。
16)在使用改进的冒泡排序算法时,如果完成了一次完整的遍历且没有进行任何交换,那么算法知道列表已经排序,不需要进一步的遍历。
17)在使用选择排序算法时,如果你希望按降序排序一个列表,你需要搜索最大值。
18)选择排序算法在内存有限的计算机系统上表现良好。
19)选择排序算法适用于大规模数据操作。
20)选择排序算法是一个非常复杂的算法。
21)插入排序算法通常比选择排序和冒泡排序算法表现更好。
22)插入排序算法有时甚至可能比快速排序算法更快。
23)快速排序算法被认为是最好的和最快的排序算法之一。
24)一个排序好的列表只包含彼此不同的元素。
25)搜索算法是一种在数据集中搜索具有特定特征的项的算法。
26)顺序查找算法只能用于包含算术值的列表。
27)最常用的搜索算法之一是快速搜索算法。
28)有一种搜索算法称为堆算法。
29)线性(或顺序)搜索算法可以如下工作:它可以检查列表的最后一个元素是否等于给定值,然后检查倒数第二个元素,依此类推,直到列表的开头或找到给定值。
30)在某些情况下,线性搜索算法可以比二分查找算法更快地找到元素。
31)线性搜索算法可以用于大规模数据操作。
32)线性搜索算法不能用于排序列表。
33)二分查找算法可以用于大规模数据操作。
34)如果一个列表中某个值出现多次,二分查找算法只能找到该值首次出现的顺序。
35)在使用搜索算法时,如果列表包含唯一值并且找到了你要查找的元素,就没有必要再进一步检查。
36)二分查找算法的主要缺点是需要对数据进行排序。
37)二分查找算法只能用于包含算术值的列表。
38)如果你要查找的元素位于列表的最后一个位置,一个从列表开头开始的线性搜索算法将检查列表中的所有元素。
39)线性搜索算法可以用于二维列表。
40)如果你使用二分查找算法查找的元素位于至少有三个元素的列表的第一个位置,它将在一次迭代中找到。
33.8 复习练习
完成以下练习。
1)编写与以下流程图片段相对应的 Python 程序。

2)设计一个流程图并编写相应的 Python 程序,允许用户将 50 个正数值输入到一个列表中。然后,算法和相应的 Python 程序必须创建一个新的包含 47 个元素的列表。在这个新列表中,每个位置必须包含四个元素的平均值:用户提供的列表中当前位置及其后三个位置的值。
3)编写一个 Python 程序,允许用户将数值输入到 a、b 和 c 列表中,每个列表有 15 个元素。然后,程序必须创建一个新的包含 15 个元素的列表 newArr。在这个新列表中,每个位置必须包含列表 a、b 和 c 对应位置的最低值。
接下来,只为创建列表 newArr 的那部分程序设计相应的流程图片段。
4)编写一个 Python 程序,允许用户将数值输入到列表 a、b 和 c 中,分别包含 10、5 和 15 个元素。然后程序必须创建一个包含 30 个元素的新列表 newArr。在这个新列表中,前 15 个位置必须包含列表 c 的元素,接下来的五个位置必须包含列表 b 的元素,最后的十个位置必须包含列表 a 的元素。
接下来,设计相应的流程图片段,仅针对创建列表 newArr 的部分。
5)编写一个 Python 程序,对于两个给定的列表 a 和 b,分别包含 3×4 和 5×4 个元素,创建一个包含 8×4 个元素的新列表 newArr。在这个新列表中,前 3 行必须包含列表 a 的元素,接下来的 5 行必须包含列表 b 的元素。
6)编写一个 Python 程序,允许用户将数值输入到列表 a、b 和 c 中,分别包含 5×10、5×15 和 5×20 个元素。然后程序必须创建一个包含 5×45 个元素的新列表 newArr。在这个新列表中,前 10 列必须包含列表 a 的元素,接下来的 15 列必须包含列表 b 的元素,最后的 20 行必须包含列表 c 的元素。
7)编写一个 Python 程序,允许用户输入 50 个数值到列表中,然后创建两个新的列表,reals 和 integers。列表 reals 必须包含实数值,而列表 integers 必须包含整数值。值 0(如果有)不得添加到任何最终列表中,无论是 reals 还是 integers。
接下来,设计相应的流程图片段,仅针对创建列表 reals 和 integers 的部分。
8)编写一个 Python 程序,允许用户输入 50 个三位整数到列表中,然后创建一个新列表,只包含第一位数字小于第二位数字,第二位数字小于第三位数字的整数。例如,357、456 和 159 是这样的整数。
9)一家民意调查公司要求 200 位公民对 10 个消费品进行评分。编写一个 Python 程序,提示用户输入每个产品的名称和每位公民给出的评分(A、B、C 或 D)。然后程序必须计算并显示以下内容:
a)对于每个产品,产品的名称和给出“A”评分的公民数量
b)对于每位公民,他们给出的“B”回答的数量
c)哪些产品被认为是最棒的
此外,使用循环控制结构,当用户输入除 A、B、C 或 D 以外的任何评分时,程序必须验证数据输入并显示错误信息。
10)编写一个 Python 程序,提示用户输入 20 个美国城市和 20 个加拿大城市的名称,然后对于每个美国城市,从每个加拿大城市到该城市的距离(以英里为单位)。最后,程序必须显示每个美国城市最近的加拿大城市。
-
设计一个流程图并编写相应的 Python 程序,允许用户输入 30 座山峰的名称和高度,以及每座山峰所属的国家。算法以及随之而来的 Python 程序必须显示有关最高和最低山峰的所有可用信息。
-
设计一个算法的流程图片段,对于给定的 N × M 元素列表 A,找到并显示最大值以及该值所在的行和列。
-
26 支球队参加足球锦标赛。每个队打 15 场比赛,每周一场。编写一个 Python 程序,允许用户输入每个队的名称以及每场比赛的“W”代表胜利、“L”代表失败和“T”代表平局(平局)。如果胜利得到 3 分,平局得到 1 分,Python 程序必须找到并显示根据哪个队获得的最大总分赢得冠军的队伍名称。假设没有两支队伍的得分相等。
-
在地球上,自由落体物体的加速度为 9.81 m/s² 向下。这个值用 g 表示。一名学生想通过实验来计算这个值。她允许 10 个不同的物体从已知高度落下,并测量它们到达地面的时间。然而,由于她的计时器不够准确,她对每个物体都进行了 20 次测量。她需要一个 Python 程序,允许她输入物体从哪个高度落下以及它们到达地面的测量时间。程序必须然后
►计算 g 并将所有计算值存储在一个 10 × 20 列表中。
►找到并显示每个物体计算出的 g 的最小和最大值。
►找到并显示所有物体计算出的 g 的总体最小和最大值。
需要的公式是
.
其中
►S 是自由落体物体所经过的距离,单位为米(m)
►u[o] 是自由落体物体的初始速度(速度),单位为每秒米(m/sec)。然而,由于自由落体物体是从静止开始的,因此 u[0] 的值必须为零。
►t 是自由落体物体到达地面的时间,单位为秒(sec)
►g 是加速度,单位为每秒平方米(m/sec²)
-
十个测量站,每个城市一个,记录一年内每天的 CO[2] 水平。编写一个 Python 程序,允许用户输入每个城市的名称以及每天同一小时记录的 CO[2] 水平。Python 程序然后显示平均大气最清新的城市名称。
-
设计一个算法的流程图片段,对于给定的 N × M 元素列表 A,找到并显示每行的最小和最大值。
-
编写一个 Python 程序,允许用户输入一个 20 × 30 列表中的值,然后找到并显示每列的最小和最大值。
18)20 支队伍参加足球锦标赛,每支队伍进行 10 场比赛,每周一场。编写一个 Python 程序,提示用户输入每支队伍的名字以及每场比赛的“W”(胜利)、“L”(失败)和“T”(平局)字母。如果胜利得到 3 分,平局得到 1 分,Python 程序必须根据哪个队伍获得的最大总分来找到并显示获得金牌、银牌和铜牌的队伍名称。使用修改后的冒泡排序算法。假设没有两支队伍的总分相同。
此外,使用循环控制结构,程序必须验证数据输入,并在用户输入除 W、L 或 T 以外的任何字母时显示错误信息。
提示:由于只需要找到前三名团队,而不是进行 19 次遍历(共有 20 支队伍),修改后的冒泡排序算法只需进行 3 次遍历。
19)编写一个 Python 程序,提示用户输入 50 个人的名字和身高。然后程序必须按身高降序显示这些信息。在两个或更多人身高相同的情况下,他们的名字必须按字母顺序显示。为了实现这一点,使用相应的冒泡排序算法。
20)编写一个 Python 程序,提示用户输入 50 个人的名、姓和父姓。然后程序必须按姓氏显示这些信息。在两个或更多人姓氏相同的情况下,他们的名字必须按字母顺序显示。另外,如果两个或更多人名字相同,他们的父姓必须按字母顺序显示。为了实现这一点,使用相应的冒泡排序算法。
21)在一场歌唱比赛中,有 10 位评委,每位评委为 12 位艺术家的表演打分。然而,根据这场比赛的规则,总分是在排除最高分和最低分之后计算的。编写一个 Python 程序,提示用户输入艺术家的名字和每位评委给出的分数。然后程序必须显示
a)对于每位艺术家,他们的名字和总分,在排除最高分和最低分之后。假设每位艺术家的最高分和最低分是唯一的,这意味着他们不会有相同值的多个分数。
b)最终的分类,从得分最高的艺术家开始。然而,如果有两个或更多艺术家的得分相同,他们的名字必须按字母顺序显示。使用相应的冒泡排序算法。
22)设计一个算法的流程图片段,该算法对于给定的 20 × 8 元素列表 A,使用冒泡排序算法按降序对每一行进行排序。
23)设计一个算法的流程图片段,该算法对于给定的 5 × 10 元素列表 A,使用冒泡排序算法按升序对每一列进行排序。
24)设计一个算法的流程图片段,该算法对于给定的 20 × 8 元素列表 A,使用插入排序算法按降序对每一行进行排序。
25)设计一个算法的流程图片段,该算法对于给定的 5 × 10 元素列表 A,使用选择排序算法按升序对每一列进行排序。
26)在一个数独比赛中,10 名参赛者竞争以尽可能快地解决八个不同的数独谜题。编写一个 Python 程序,让用户输入每位参赛者的姓名以及他们完成每个谜题的时间(以小时、分钟和秒为单位)。然后程序必须显示
a)对于每位参赛者,他们的姓名以及他们的三个最佳时间。假设每位参赛者的时间都不同。
b)根据平均时间最低的参赛者,获得金牌、银牌和铜牌的三位参赛者的姓名。假设没有两位参赛者的平均时间相同。
在必要时使用选择排序算法。
提示:鉴于只需找到三名最佳参赛者,选择排序算法只需对前三个元素进行排序。
27)五个测量站,每个大型城市的每个区域一个,每小时记录一次每日二氧化碳(CO[2])水平。编写一个 Python 程序,让用户输入每个区域的名称以及每小时的 CO[2]水平(从 00:00 到 23:00),为期两天。然后 Python 程序必须计算并显示
a)对于每个区域,其名称及其平均 CO[2]水平
b)对于每个小时,城市的平均 CO[2]水平
c)城市大气污染最严重的时区(平均而言)
d)记录最高 CO[2]水平的时区和小时
e)使用插入排序算法对最脏的三个区域(平均而言)进行排序
28)设计线性搜索算法的流程图片段,该算法在包含 N 个元素的列表 a 上执行搜索以找到值针,并显示针找到的位置索引。如果针未找到,必须显示消息“未找到”。
29)设计二分搜索算法的流程图片段,该算法在包含 N 个元素的列表 a 上执行搜索以找到值针,并显示针找到的位置。如果针未找到,必须显示消息“未找到”。
30)十二支队伍参加足球锦标赛,每支队伍每周进行 20 场比赛。编写一个 Python 程序,提示用户输入每支队伍的名称以及每场比赛的“W”(胜利)、“L”(失败)或“T”(平局)字母。然后程序必须提示用户输入一个字母(W、L 或 T),并显示每个队伍赢得、输掉或平局的周数。例如,如果用户输入“L”,Python 程序必须搜索并显示每个队伍输掉比赛的周数(例如,第 3 周、第 14 周等)。
- 10 支队伍参加足球锦标赛,每支队伍每周进行一场比赛,共 16 场比赛。编写一个 Python 程序,提示用户输入每支队伍的名称、每场比赛球队进球数和每场比赛球队失球数。胜利获得 3 分,平局获得 1 分。然后 Python 程序必须提示用户输入队伍名称,并最终计算并显示该队的总积分。如果用户提供的队伍名称不存在,必须显示消息“该队伍不存在”。
此外,使用循环控制结构,程序必须验证数据输入,并在用户输入任何负数进球数时显示错误消息。
假设没有两个队伍共享相同的名称。
- 在一所高中,有两个班级,分别有 20 名和 25 名学生。编写一个 Python 程序,提示用户分别输入两个班级的学生姓名。然后程序以升序独立显示每个班级的姓名。之后,程序提示用户输入一个姓名,并在两个列表中搜索该用户提供的姓名。如果找到学生的姓名,程序必须显示消息“学生在第 N 班找到”,其中 N 可以是 1 或 2;否则,必须显示消息“学生未在任何班级中找到”。假设两个列表都包含唯一的姓名。
提示:由于列表已排序且名称是唯一的,请使用二分查找算法。
-
假设有两个列表,分别是用户名和密码,它们包含一家公司 100 名员工的登录信息。编写一个代码片段,提示用户输入用户名和密码,当用户名和密码组合有效时,显示消息“登录成功!”;否则,必须显示消息“登录失败!”。用户名和密码都不区分大小写。假设用户名是唯一的,但密码不是。
-
假设有两个列表,分别是姓名和 SSN(社会保险号),它们包含 1,000 名美国公民的姓名和 SSN。编写一个代码片段,提示用户输入一个值(可以是姓名或 SSN),然后搜索并显示所有具有该姓名或 SSN 的人的姓名。如果用户提供的值不存在,必须显示消息“此值不存在”。
-
有 12 名学生,他们每个人都已经收到了六节课的成绩。编写一个 Python 程序,让用户输入所有课程的成绩,然后显示一条消息,指示是否至少有一名学生的平均成绩低于 70 分。此外,使用循环控制结构,程序必须验证数据输入,并在用户输入任何负值或大于 100 的值时,显示不同类型的错误消息。
36)编写一个 Python 程序,提示用户输入一个英文信息,然后,使用下面的表格,显示相应的摩尔斯电码,使用点和划。请注意,在翻译的信息中,空格字符必须显示为斜杠字符(/)。
| 摩尔斯电码 |
|---|
| A |
| B |
| C |
| D |
| E |
| F |
| G |
| H |
| 我 |
| J |
| K |
| L |
| M |
提示:使用字典来存储摩尔斯电码。
37)编写一个 Python 程序,提示用户输入一个英文句子。程序必须显示:
a)用户提供的句子中存在的所有字母及其频率计数
b)用户提供的句子中不存在的所有字母
c)与英语字母表中的字母相比,不存在字母的百分比
d)与用户提供的句子中的字符相比(不包括空格字符),非字母字符的百分比
提示:使用字典存储所有 26 个英语字母作为键,它们的频率作为值,但找到一种巧妙的方法来创建它。
第四十二章:在“Python 中的数据结构”中进行复习
复习填字游戏
- 解决以下填字游戏。

Across
-
它的元素可以使用键唯一标识,而不一定是整数值。
-
这些括号用于创建一个列表。
-
排序算法。
-
在这个对角线上,元素的行索引等于它们的列索引。
-
这种排序算法在主内存(RAM)有限的计算机系统上表现良好。
-
在方阵中,从右上角到左下角运行的元素集合是什么?
-
数据 _________ 是一组组织良好的数据,以便您可以以最有效的方式对其执行操作。
-
Python 中的一个可变数据结构。
-
将列表的元素放入特定顺序的过程。
Down
-
一个 Python 数据结构。
-
这种排序算法在排序非常小的列表时可以非常快,有时甚至比快速排序算法还要快。
-
它被认为是最好的和最快的排序算法之一。
-
搜索算法。
-
另一个名称是顺序搜索算法。
-
从列表中选择一系列元素的一种机制。
-
每个列表元素被分配一个唯一的数字,称为 _________。
复习问题
回答以下问题。
-
变量有什么限制,而列表没有?
-
什么是数据结构?
-
数据结构中的每个元素被称为什么?
-
列出 Python 支持的六个已知数据结构。
-
Python 中的列表是什么?
-
Python 中的字典是什么?
-
当我们说列表是“可变的”时,这意味着什么?
-
当一个语句尝试显示一个不存在的列表元素的值时会发生什么?
-
当一个语句尝试将值分配给一个不存在的字典元素时会发生什么?
-
在一个包含 100 个元素的列表中,最后一个元素的索引是什么?
-
“遍历行”是什么意思?
-
“遍历列”是什么意思?
-
什么是方阵?
-
方阵的主对角线是什么?
-
方阵的反对角线是什么?
-
编写一个通用形式的代码片段,用于验证列表的数据输入而不显示任何错误信息。
-
编写一个通用形式的代码片段,用于验证列表的数据输入并显示一个通用的错误信息(即,对于任何类型的输入错误显示相同的错误信息)。
-
编写一个通用形式的代码片段,用于验证列表的数据输入并针对每种输入错误显示不同的错误信息。
-
什么是排序算法?列出五种排序算法。
-
哪种排序算法被认为是最不高效的?
-
是否可以使用排序算法来找到列表的最小值或最大值?
-
为什么排序算法不是找到列表最小值或最大值的最优选择?
-
编写一个代码片段,使用冒泡排序算法按升序排序包含 N 个元素的列表 a。
24)对于给定包含 N 个元素的列表,冒泡排序算法执行了多少次比较?
25)冒泡排序算法在何时执行最大数量的交换操作?
26)使用冒泡排序算法,编写一个代码片段,对列表 a 进行排序,但保持与列表 b 中 N 个元素的升序一对一对应。
27)使用改进的冒泡排序算法,编写一个代码片段,将包含 N 个元素的列表 a 按升序排序。
28)使用选择排序算法,编写一个代码片段,用于将包含 N 个元素的列表 a 按升序排序。
29)使用插入排序算法,编写一个代码片段,将包含 N 个元素的列表 a 按升序排序。
30)什么是搜索算法?请列举两种最常用的搜索算法。
31)线性查找算法的优点和缺点是什么?
32)使用线性查找算法,编写一个代码片段,在列表 a 上执行搜索以找到值 needle。
33)二分查找算法的优点和缺点是什么?
34)使用二分查找算法,编写一个代码片段,在列表 a 上执行搜索以找到值 needle。假设列表已按升序排序。
第七部分
子程序
第三十四章
子程序简介
34.1 什么是子程序?
在计算机科学中,子程序是一块被封装成单元的语句块,执行特定的任务。子程序可以在程序中被调用多次,每当需要执行该特定任务时。
在 Python 中,内置函数就是一个这样的子程序示例。以众所周知的 abs()函数为例。它由一个名为“abs”的单元封装的语句块组成,并执行特定的任务——它返回一个数的绝对值。
如果你想知道函数 abs()内部可能存在哪些语句,这里是一个可能的语句块。
if number < 0:
返回数值 * (-1)
else:
返回数值
通常来说,有两种类型的子程序:函数和过程。函数和过程之间的区别在于函数返回一个结果,而过程不返回。然而,在某些计算机语言中,这种区别可能并不明显。有些语言中,函数也可以表现得像过程并且不返回结果,而有些语言中,过程可以返回一个或甚至多个结果。
根据所使用的计算机语言,术语“函数”和“过程”可能不同。例如,在 Visual Basic 中你可以找到它们作为“函数”和“子过程”,在 FORTRAN 中作为“函数”和“子例程”,而在 Python 中,通常首选的术语是“函数”和“void 函数”。
34.2 什么是过程式编程?
假设你被分配了一个项目来解决你所在地区的药物滥用问题。一个可能的方法(这可能非常困难甚至不可能)就是试图自己解决这个问题!
然而,更好的方法是将大问题细分为更小的子问题,如预防、治疗和康复,每个子问题都可以进一步细分为更小的子问题,如图 34–1 所示。

图 34–1 一个问题可以被细分为更小的问题
作为这个项目的管理者,你可以租用一栋楼并在其中建立三个部门:预防部门,包括其所有子部门;治疗部门,包括其所有子部门;以及康复部门,包括其所有子部门。最后,你会雇佣员工(来自各个领域的专家),你会组建团队并雇佣他们为你完成工作!
过程式编程正是如此。它将一个初始问题细分为更小的子问题,每个子问题进一步细分为更小的子问题。最后,为每个子问题编写一个小子程序,主程序(就像管理者一样),调用(雇佣)它们来完成不同的工作部分。
过程式编程提供了一些优点:
►它使程序员在必要时能够重用相同的代码,而无需重新编写或复制。
►它相对容易实现。
►它有助于程序员更容易地遵循执行流程,简化调试过程。
当一个非常大的程序全部集中在一块时,调试和维护可能会非常困难。因此,将其细分为更小的子程序通常更容易,每个子程序执行一个明确定义的过程。
34.3 什么是模块化编程?
在模块化编程中,具有共同功能的子程序可以组合成单独的模块,并且每个模块可以有自己的数据集。因此,一个程序可以由多个部分组成,而这些部分(模块)中的每一个都可以包含一个或多个更小的部分(子程序)。
数学模块就是这样一种例子。它包含与数学相关的共同功能子程序(如 fsum()、sqrt()、sin()、cos()、tan()等)。
如果在先前的药物问题示例中使用模块化编程,那么可以有三个独立的建筑——一个用于预防部门及其所有子部门,第二个用于治疗部门及其所有子部门,第三个用于康复部门及其所有子部门(如图 34-2 所示)。这三个建筑可以被视为模块化编程中的三个不同模块,每个模块都包含具有共同功能的子程序。

图 34-2 具有共同功能的子程序可以组合成单独的模块。
34.4 复习问题:正确/错误
对以下每个陈述选择正确或错误。
1)子程序是一块打包成单元的语句块,执行特定任务。
2)一般来说,有两种子程序:函数和过程。
3)一般来说,函数和过程的区别在于过程返回一个结果,而函数则不返回。
4)Python 只支持过程。
5)过程式编程将初始问题细分为更小的子问题。
6)过程式编程的一个优点是能够重用相同的代码,而无需重新编写或复制它。
7)过程式编程有助于程序员更容易地遵循执行流程。
8)模块化编程可以提高程序开发速度。
9)在模块化编程中,具有共同功能的子程序被组合成单独的模块。
10)在模块化编程中,每个模块可以有自己的数据集。
11)模块化编程使用与结构化编程不同的结构。
12)一个程序可以由多个模块组成。
第三十五章
用户定义的子程序
35.1 返回值的子程序
在 Python 和许多其他计算机语言中,返回值的子程序称为函数。Python 中有两种函数类别。有内置函数,如 int()、float(),还有用户定义函数,即你可以亲自编写并在自己的程序中使用它们。
返回一个或多个值的 Python 函数的一般形式如下所示。
def name([arg1, arg2, arg3, …]):
一个语句或语句块
return value1 [, value2, value3, … ]
其中
►name 是函数的名称。
►arg1、arg2、arg3、… 是参数(变量、列表等)的列表,用于从调用者传递值到函数。可以有你需要那么多参数。
►value1, value2, value3, … 是返回给调用者的值。它们可以是常量值、变量、表达式,甚至是数据结构。
注意,参数是可选的;也就是说,一个函数可能不包含任何参数。
在第 5.4 节中,你学习了在给变量命名时必须遵循的规则。给子程序命名遵循完全相同的规则!
函数名称可以比作一个盒子(见图 35–1),其中包含一个语句或语句块。它接受 arg1、arg2、arg3、…作为输入值,并返回 value1、value2、value3、…作为输出值。

图 35–1 函数可以比作一个盒子
例如,下一个函数通过参数 num1 和 num2 接受两个数字,然后计算它们的和并返回结果。
def getSum(num1, num2):
result = num1 + num2
return result
当然,这也可以写成
def getSum(num1, num2):
return num1 + num2
下一个函数计算两个数字的和与差,并返回结果。
def getSumDif(num1, num2):
s = num1 + num2
d = num1 - num2
return s, d
35.2 如何调用函数
每次调用函数都是这样的:你写下函数的名称,后面跟着一个参数列表(如果需要),要么在一个将函数返回值分配给变量的语句中,要么直接在表达式中。
让我们看看一些例子。以下函数通过参数 num 接受一个数值,然后返回该数值的三次幂的结果。
def cube(num):
result = num ** 3
return result
现在,假设你想计算以下表达式的结果
.
你可以将函数 cube()返回的值分配给一个变量,如下所示
x = float(input())
cb = cube(x) #将返回的值分配给变量
y = cb + 1 / x #并使用该变量
print(y)
或者你可以在表达式中直接调用函数,
x = float(input())
y = cube(x) + 1 / x #直接在表达式中调用函数
print(y)
或者你甚至可以直接在 print()语句中调用函数。
x = float(input())
print(立方(x) + 1 / x) #直接在 print()语句中调用函数
用户定义的函数可以像 Python 的内置函数一样调用。
现在让我们看看另一个例子。下一个 Python 程序定义了函数 getMessage(),然后主代码调用它。返回值被分配给变量 a。
file_35.2a
定义函数
def 获取消息():
msg = "你好,宙斯"
返回消息
主代码从这里开始
print("你好!")
a = 获取消息()
print(a)
如果你运行这个程序,将显示以下消息。
.
函数在程序开始运行时不会立即执行。在上一个例子中,实际执行的第一个语句是 print("你好!")。
你可以向函数传递(发送)值,只要函数的括号内至少有一个参数存在。在下一个例子中,函数 display()被调用了三次,但每次通过参数颜色传递了不同的值。
file_35.2b
定义函数
def 显示(颜色):
msg = "彩虹中有" + 颜色 + "的"
返回消息
主代码从这里开始
print(显示("红色"))
print(显示("黄色"))
print(显示("蓝色"))
如果你运行这个程序,将显示以下消息。

在下一个例子中,必须向函数 display()传递两个值。
file_35.2c
def 显示(颜色, exists):
neg = ""
if not exists:
neg = "n't any"
return "There is" + neg + " " + 颜色 + " in the rainbow"
主代码从这里开始
print(显示("红色", True))
print(显示("黄色", True))
print(显示("黑色", False))
如果你运行这个程序,将显示以下消息。

在 Python 中,你必须将你的子程序放在你的主代码之上。在其他计算机语言中,例如 Java 或 PHP,你可以将你的子程序放在主代码之上或之下。即使如此,然而,大多数程序员更喜欢将所有子程序放在顶部,以便更好地观察。
正如之前提到的,Python 中的函数可以返回多个值。下一个例子提示用户输入他们的名字和姓氏,然后显示它们。
file_35.2d
定义函数
def 获取全名():
firstName = input("输入名字: ")
lastName = input("输入姓氏: ")
return firstName, lastName
主代码从这里开始
fname, lname = 获取全名()
print("名字:", fname)
print("姓氏:", lname)
35.3 不返回值的子程序
在计算机科学中,不返回值的子程序可以被称为过程、子过程、子程序、空函数、空方法等。在 Python 中,通常首选的术语是空函数。
Python 空函数的一般形式是
def name([arg1, arg2, arg3, …]):
一个语句或语句块
where
►name 是空函数的名称。
►arg1, arg2, arg3, … 是用于从调用者传递值到空函数的参数(变量、列表等)列表。可以有任意多的参数。
请注意,参数是可选的;也就是说,空函数可以不包含任何参数。
例如,下一个空函数通过参数 num1 和 num2 接收两个数字,然后计算它们的和,最后显示结果。
def displaySum(num1, num2):
result = num1 + num2
print(result)
35.4 如何调用空函数
您可以通过仅写出其名称来调用空函数。下一个示例定义了空函数 displayLine() 和主代码在需要显示水平线时调用空函数。
file_35.4a
定义空函数
def displayLine():
print("-----------------------------")
主代码从这里开始
print("Hello there!")
displayLine()
print("How do you do?")
displayLine()
print("What is your name?")
displayLine()
您可以向空函数传递(发送)值,只要在空函数的括号内至少存在一个参数。在下一个示例中,空函数 displayLine() 被调用了三次,但每次通过变量 length 传递了不同的值,结果打印了三条不同长度的线条。
file_35.4b
def displayLine(length):
print("-" * length)
主代码从这里开始
print("Hello there!")
displayLine(12)
print("How do you do?")
displayLine(14)
print("What is your name?")
displayLine(18)
由于空函数 displayLine() 不返回任何值,以下代码行是错误的。您不能将空函数赋值给变量,因为没有返回值!
y = display_line(12)
此外,您不能在语句中调用它。以下代码行也是错误的。
print("Hello there!\n", display_line(12))
35.5 形式参数和实际参数
每个函数(或空函数)都包含一个称为形式参数列表的参数列表。如前所述,此列表中的参数是可选的;形式参数列表可以不包含任何参数,包含一个参数,或包含多个参数。
当调用子程序(函数或空函数)时,可以向子程序传递一个参数列表。此列表称为实际参数列表。
在下一个示例中,形式参数(变量)n1 和 n2 构成形式参数列表,而形式参数(变量)x 和 y,以及形式参数(表达式)x + y 和 y / 2,构成实际参数列表。
file_35.5
定义函数 multiply()。
n1 和 n2 这两个参数被称为形式参数。
def multiply( n1, n2 ): [更多…]
result = n1 * n2
return result
主代码从这里开始
x = float(input())
y = float(input())
调用 multiply()函数。
两个参数 x 和 y 被称为实际参数。
w = multiply( x, y ) [更多…]
print(w)
调用 multiply()函数。
两个参数 x + y 和 y / 2 被称为实际参数。
print(multiply( x + 2, y / 2 )) [更多…]
注意形式参数和实际参数之间存在一对一的对应关系。在第一次调用中,实际参数 x 的值传递给形式参数 n1,实际参数 y 的值传递给形式参数 n2。在第二次调用中,实际参数(表达式结果)x + 2 的值传递给形式参数 n1,实际参数(表达式结果)y / 2 的值传递给形式参数 n2。
35.6 函数是如何执行的?
当主代码调用函数时,以下步骤被执行:
►主代码的语句执行被中断。
►变量值或实际参数列表中存在的表达式的结果被传递(分配)到形式参数列表中对应的参数(变量)中,执行流程转到函数定义的位置。
►函数的语句被执行。
►当执行流程到达返回语句时,一个或多个值从函数返回到主代码,执行流程从调用函数之前的位置继续。
在下一个 Python 程序中,函数 maximum()接受两个参数(数值)并返回两个值中较大的一个。
file_35.6
def maximum(val1, val2):
m = val1
if val2 > m:
m = val2
return m
主代码从这里开始
a = float(input())
b = float(input())
maxim = maximum(a, b)
print(maxim)
当 Python 程序开始运行时,第一条执行的语句是 a = float(input())(这被认为是程序的第一条语句)。
下面是一个跟踪表,显示了执行流程的确切流程,变量 a 和 b 的值是如何从主代码传递到函数中,以及函数是如何返回其结果的。假设用户输入的值是 3 和 8。
| 步骤 | 主代码的语句 | a | b | maxim |
|---|---|---|---|---|
| 1 | a = float(input()) | 3.0 | ? | ? |
| 2 | b = float(input()) | 3.0 | 8.0 | ? |
| 3 | maxim = maximum(a, b) |
当调用函数 maximum()时,主代码的语句执行被中断,变量 a 和 b 的值被传递(分配,如果你愿意)到相应的形式参数(变量)val1 和 val2,执行流程转到函数定义的位置。然后执行函数的语句。
| 步骤 | 函数 maximum()的语句 | val1 | val2 | m |
|---|---|---|---|---|
| 4 | m = val1 | 3.0 | 8.0 | 3.0 |
| 5 | if val2 > m: | This evaluates to True | ||
| 6 | m = val2 | 3.0 | 8.0 | 8.0 |
| 7 | 返回 m |
当执行流程达到返回语句时,函数从函数返回值 8 到主代码(并分配给变量 maxim),然后执行流程从调用函数之前的地方继续。主代码在用户的屏幕上显示值 8。
| 步骤 | 主代码的语句 | a | b | maxim |
|---|---|---|---|---|
| 8 | 打印(maxim) | 3.0 | 8.0 | 8.0 |
练习 35.6-1 回归基础 - 计算两个数的和
执行以下操作:
i)编写一个名为 total 的子程序,它通过其形式参数列表接受两个数值,然后计算并返回它们的和。
ii)使用上述子程序,编写一个 Python 程序,允许用户输入两个数字,然后显示它们的和。接下来,创建一个跟踪表以确定 Python 程序每个步骤中变量的值,针对两次不同的执行。
两次执行的输入值分别是:(i) 2, 4;和(ii) 10, 20。
解答
在这个练习中,你需要编写一个函数,它接受调用者提供的两个值(这是主代码),然后计算并返回它们的和。解答在这里展示。
file_35.6-1
def total(a, b):
s = a + b
return s
主代码从这里开始
num1 = float(input())
num2 = float(input())
result = total(num1, num2)
print("The sum of", num1, "+", num2, "is", result)
现在,让我们创建相应的跟踪表。由于你已经更熟悉它们,所以已经移除了“备注”列。
i) 对于输入值 2 和 4,跟踪表看起来如下。
| 步骤 | 语句 | 主代码 | 函数 total() |
|---|---|---|---|
| num1 | num2 | 结果 | a |
| 1 | num1 = float(input()) | 2.0 | ? |
| 2 | num2 = float(input()) | 2.0 | 4.0 |
| 3 | result = total(num1, num2) | ||
| 4 | s = a + b | ||
| 5 | 返回 s | 2.0 | 4.0 |
| 6 | 打印("The sum of… | 它显示:The sum of 2.0 + 4.0 is 6.0 |
ii)对于输入值 10 和 20,跟踪表如下。
| 步骤 | 语句 | 主代码 | 函数 total() |
|---|---|---|---|
| num1 | num2 | 结果 | a |
| 1 | num1 = float(input()) | 10.0 | ? |
| 2 | num2 = float(input()) | 10.0 | 20.0 |
| 3 | result = total(num1, num2) | ||
| 4 | s = a + b | ||
| 5 | 返回 s | 10.0 | 20.0 |
| 6 | 打印("The sum of… | 它显示:The sum of 10.0 + 20.0 is 30.0 |
练习 35.6-2 使用更少的代码行计算两个数的和!
使用更少的代码行重写上一个练习的 Python 程序。
解答
解答在这里展示。
file_35.6-2
def total(a, b):
return a + b
主代码从这里开始
num1 = float(input())
num2 = float(input())
print("The sum of", num1, "+", num2, "is", total(num1, num2))
与前一个练习的解决方案相反,在这个方法 total()中,总和不是分配给变量 s,而是直接计算并返回。此外,在这个主代码中,返回的值不是分配给变量,而是直接显示。
用户定义的函数可以像 Python 的内置函数一样调用。
35.7 如何执行空函数?
当主代码调用空函数时,执行以下步骤:
►主代码的语句执行被中断。
►实际参数列表中存在的变量值或表达式的结果被传递(分配)到形式参数列表中相应的参数(变量)中,执行流程转到空函数的编写处。
►空函数的语句被执行。
►当执行流程达到空函数的末尾时,执行流程从调用空函数之前的位置继续。
在下一个 Python 程序中,空函数 minimum()通过其形式参数列表接受三个参数(数值)并显示最小值。
file_35.7
def minimum(val1, val2, val3):
minim = val1
if val2 < minim:
minim = val2
if val3 < minim:
minim = val3
print(minim)
主代码从这里开始
a = float(input())
b = float(input())
c = float(input())
minimum(a, b, c)
print("The end")
当 Python 程序开始运行时,首先执行的是语句 a = float(input())(这被认为是程序的第一条语句)。假设用户输入的值是 9、6 和 8。
| 步骤 | 主代码的语句 | a | b | c |
|---|---|---|---|---|
| 1 | a = float(input()) | 9.0 | ? | ? |
| 2 | b = float(input()) | 9.0 | 6.0 | ? |
| 3 | c = float(input()) | 9.0 | 6.0 | 8.0 |
| 4 | minimum(a, b, c) |
当调用空函数 minimum()时,主代码的语句执行被中断,变量 a、b 和 c 的值被传递(分配)到相应的形式参数(变量)val1、val2 和 val3,然后执行空函数的语句。
| 步骤 | 空函数 minimum()的语句 | val1 | val2 | val3 | minim |
|---|---|---|---|---|---|
| 5 | minim = val1 | 9.0 | 6.0 | 8.0 | 9.0 |
| 6 | if val2 < minim: | 这评估为 True | |||
| 7 | minim = val2 | 9.0 | 6.0 | 8.0 | 6.0 |
| 8 | if val3 < minim: | 这评估为 False | |||
| 9 | print(minim) | 它显示:6.0 |
当执行流程达到空函数的末尾时,执行流程简单地从调用空函数之前的位置继续。
| 步骤 | 主代码的语句 | a | b | c |
|---|---|---|---|---|
| 10 | print("The end") | 它显示:The end |
注意在步骤 9 和步骤 10 之间,从空函数中没有返回任何值到主代码。
练习 35.7-1 回归基础 – 显示一个数的绝对值
执行以下操作:
i)编写一个名为 displayAbs 的子程序,它通过形式参数列表接受一个数值,然后显示其绝对值。不要使用 Python 的内置 abs()函数。
ii)使用上述子程序,编写一个 Python 程序,允许用户输入一个数字,然后显示其绝对值,接着显示用户提供的值。接下来,创建跟踪表以确定 Python 程序两次不同执行中每个步骤的变量值。
两次执行的输入值分别为:(i) 5,和(ii) −5。
解决方案
在这个练习中,你需要编写一个无返回值函数,该函数接受调用者传递的值(这是主代码部分)然后计算并显示其绝对值。解决方案如下所示。
file_35.7-1
def displayAbs(n):
if n < 0:
n = (-1) * n
print(n)
主代码从这里开始
a = float(input())
displayAbs(a) # 这显示用户提供的数字的绝对值。
print(a) # 这显示用户提供的数字。
现在,让我们创建相应的跟踪表。
i)对于输入值 5,跟踪表如下所示。
| 9)以下语句是一个有效的 Python 语句。 |
|---|
| a |
| 1 |
| 2 |
| 3 |
| 步骤 |
| 5 |
ii)对于输入值−5,跟踪表如下所示。
| 步骤 | 语句 | 主代码 | 无返回值函数 displayAbs() |
|---|---|---|---|
| a | n | ||
| 1 | a = float(input()) | −5.0 | |
| 2 | displayAbs(a) | −5.0 | |
| 3 | if n < 0: | 这评估为 True | |
| 4 | n = (−1) * n | 5.0 | |
| 5 | print(n) | 它显示:5.0 | |
| 6 | print(a) | 它显示:−5.0 |
注意,在第 5 步中,无返回值函数的变量 n 包含值 5.0,但当执行流程在第 6 步返回主代码时,主代码中的变量 a 仍然包含值−5.0。实际上,主代码中变量 a 的值从未改变过!
35.8 复习问题:对/错
对以下每个陈述选择对或错。
1)Python 中有两种子程序类别,它们返回值。
2)用于将值传递给函数的变量被称为参数。
3)int()函数是一个用户定义的函数。
4)对用户定义函数的每次调用都与对 Python 内置函数的调用方式相同。
5)函数的形式参数列表可以有任意多个参数。
6)在函数中,形式参数列表必须至少包含一个参数。
7)在函数中,形式参数列表是可选的。
8)函数不能返回一个列表。
| 步骤 | 语句 | 主代码 | 无返回值函数 displayAbs() |
return x + 1
10)形式参数可以是一个表达式。
11)实际参数可以是一个表达式。
12)在 Python 中,函数可以没有实际参数列表中的参数。
13)接下来的语句调用了三次 cubeRoot()函数。
cb = cubeRoot(x) + cubeRoot(x) / 2 + cubeRoot(x) / 3
14)下面的代码片段显示的值与语句 print(cubeRoot(x)+5)完全相同
cb = cubeRoot(x)
y = cb + 5
print(y)
15)一个函数必须始终包含一个返回语句,而 void 函数则不必。
16)play-the-guitar 可以是一个有效的函数名。
17)在 Python 中,你可以将你的函数放在主代码的上方或下方。
18)当主代码调用函数时,主代码的语句执行被中断。
19)一般来说,一个函数可以不向调用者返回任何值。
20)函数 abs()是 Python 的一个内置函数。
21)下面的代码片段显示的值是 0.5。
def divide(b, a):
return a / b
a = 10.0
b = 5.0
print(divide(a, b))
22)在计算机科学中,一个不返回结果的子程序被称为 void 函数。
23)在 Python 中,你可以通过写出其名称后跟一个开括号和一个闭括号来调用 void 函数。
24)在主代码中进行的 void 函数调用中,实际参数列表中使用的变量必须是主代码中的变量。
25)在 void 函数调用中,实际参数列表中只能使用变量。
26)在 void 函数中,所有形式参数必须有不同的名字。
27)一个 void 函数必须在其形式参数列表中至少包含一个参数。
28)形式参数和实际参数之间存在一一对应关系。
29)你可以在一个语句中调用一个 void 函数。
30)当执行流程到达 void 函数的末尾时,执行流程从调用 void 函数之前的位置继续。
31)一个 void 函数不会向调用者返回任何值。
32)void 函数可以不接受任何值从调用者那里。
33)调用 void 函数的方式与调用函数的方式不同。
34)在下面的 Python 程序中,首先执行的是打印语句 print("Hello Aphrodite!").
def message():
print("Hello Aphrodite!")
print("Hi there!")
message()
35.9 复习练习
完成以下练习。
1)以下函数包含一些错误。你能找出它们吗?
def findMax(a, b)
if a > b:
maximum = a
else:
maximum = b
2)创建一个跟踪表,以确定在以下 Python 程序每一步中变量的值。
def sumDigits(a):
d1 = a % 10
d2 = a // 10
return d1 + d2
s = 0
for i in range(25, 28):
s += sumDigits(i)
print(s)
3)创建一个跟踪表,以确定在以下 Python 程序每一步中变量的值。
def sss(a):
total = 0
for k in range(1, a + 1):
total += k
return total
i = 1
s = 0
while i < 6:
if i % 2 == 1:
s += 1
else:
s += sss(i)
i += 1
print(s)
4)创建一个跟踪表,以确定在输入值为 12 时,以下 Python 程序每一步中变量的值。
def customDiv(b, d):
return (b + d) // 2
k = int(input())
m = 2
a = 1
while a < 6:
if k % m != 0:
x = customDiv(a, m)
else:
x = a + m + customDiv(m, a)
print(m, a, x)
a += 2
m += 1
5)创建一个跟踪表,以确定当输入值 3、7、9、2 和 4 时,以下 Python 程序每一步中变量的值。
def display(a):
if a % 2 == 0:
print(a, "是偶数")
else:
print(a, "是奇数")
for i in range(5):
x = int(input())
display(x)
6)创建一个跟踪表,以确定以下 Python 程序每一步中变量的值。
def division(a, b):
b = b // a
print(a * b)
x = 20
y = 30
while x % y < 30:
division(y, x)
x = 4 * y
y += 1
7)创建一个跟踪表,以确定当输入值 2、3 和 4 时,以下 Python 程序每一步中变量的值。
def calculate(n):
s = 0
for j in range(2, 2 * n + 2, 2):
s = s + j ** 2
print(s)
for i in range(3):
m = int(input())
calculate(m)
8)编写一个子程序,它通过形式参数列表接受三个数字,然后返回它们的总和。
9)编写一个子程序,它通过形式参数列表接受四个数字,然后返回它们的平均值。
10)编写一个子程序,它通过形式参数列表接受三个数字,然后返回最大值。尽量不使用 Python 的 max()函数。
11)编写一个子程序,它通过形式参数列表接受五个数字,然后显示最大值。
12)编写一个名为 myRound 的子程序,它通过形式参数列表接受一个实数,并将其四舍五入到两位小数。尽量不使用 Python 的 round()函数。
13)执行以下操作:
i)编写一个名为 findMin 的子程序,它通过形式参数列表接受两个数字,并返回较小的一个。尽量不使用 Python 的 min()函数。
ii)使用上述子程序,编写一个 Python 程序,提示用户输入四个数字,然后显示最小的数字。
14)执行以下操作:
i)编写一个名为 KelvinToFahrenheit 的子程序,它通过形式参数列表接受开尔文温度,并返回其华氏度等效值。
ii)编写一个名为 KelvinToCelsius 的子程序,它通过形式参数列表接受开尔文温度,并返回其摄氏度等效值。
iii)使用上述子程序,编写一个 Python 程序,提示用户输入开尔文温度,然后显示其华氏度和摄氏度等效值。
已知
华氏度 = 1.8 ∙ 开尔文 − 459.67
和
摄氏度 = 开尔文 − 273.15
15)身体质量指数(BMI)常用于确定一个人的体重是否超重或偏轻。计算 BMI 使用的公式是
.
执行以下操作:
i)编写一个名为 bmi 的子程序,它通过形式参数列表接受体重和身高,然后根据以下表格返回一个动作(消息)。
| BMI | 动作 |
|---|---|
| BMI < 16 | 您必须增加体重。 |
| 16 ≤ BMI < 18.5 | 您应该增加一些体重。 |
| 18.5 ≤ BMI < 25 | 保持您的体重。 |
| 25 ≤ BMI < 30 | 您应该减掉一些体重。 |
| 30 ≤ BMI | 您必须减肥。 |
ii)使用上述子程序,编写一个 Python 程序,提示用户输入他们的体重(磅)、年龄(年)和身高(英寸),然后显示相应的消息。使用循环控制结构,程序还必须验证数据输入,并在用户输入
a)体重的任何负值
b)年龄小于 18 岁的任何值
c)身高的任何负值
16)执行以下操作:
i)编写一个名为 numOfDays 的子程序,该子程序通过其形式参数列表接受一个年份和一个月(1 - 12),然后显示该月的天数。当年份是闰年时,要特别注意;即二月有 29 天而不是 28 天。
提示:如果一个年份能被 4 整除但不能被 100 整除,或者能被 400 整除,那么这个年份就是闰年。
ii)使用上述子程序,编写一个 Python 程序,提示用户输入一个年份,然后显示该年每个月的天数。
17)执行以下操作:
i)编写一个名为 numOfDays 的子程序,该子程序通过其形式参数列表接受一个年份和一个月(1 - 12),然后返回该月的天数。当年份是闰年时,要特别注意,就像你在上一个练习中所做的那样。
ii)使用上述子程序,编写一个 Python 程序,提示用户输入一个年份和两个月(1 - 12)。然后程序必须计算并显示从第一个月的第一天到第二个月的最后一天之间的总天数。
18)执行以下操作:
i)编写一个名为 displayMenu 的子程序,显示以下菜单。
1)将米转换为英里
2)将英里转换为米
3)退出
ii)编写一个名为 metersToMiles 的子程序,该子程序通过其形式参数列表接受米值,然后显示消息“XX 米等于 YY 英里”,其中 XX 和 YY 必须替换为实际值。
iii)编写一个名为 milesToMeters 的子程序,该子程序通过其形式参数列表接受英里值,然后显示消息“YY 英里等于 XX 米”,其中 XX 和 YY 必须替换为实际值。
iv)使用上述子程序,编写一个 Python 程序,显示前面提到的菜单,并提示用户输入一个选择(1、2 或 3)和一个距离。然后程序必须计算并显示所需值。该过程必须根据用户的意愿重复进行。
已知 1 英里等于 1609.344 米。
19)LAV 手机公司每月向客户收取 10 美元的基本费用,并根据客户在一个月内使用手机通话的总秒数收取额外费用。使用以下表格中的费率。
| 客户在手机上通话的秒数 | 额外费用(每秒美元) |
|---|---|
| 1 - 600 | 免费 |
| 601 - 1200 | $0.01 |
| 1201 及以上 | $0.02 |
执行以下操作:
i) 编写一个名为 amountToPay 的子程序,它通过形式参数列表接受秒数,然后显示应付的总金额。请注意,费率是递增的。此外,联邦、州和地方税将总计 11%的税额加到每一张账单上
ii) 使用上述子程序,编写一个 Python 程序,提示用户输入他们在手机上通话的秒数,然后显示应付的总金额。
第三十六章
子程序的使用技巧和窍门
36.1 两个子程序能否使用相同名称的变量?
每个子程序都使用自己的内存空间来存储其变量的值。即使是主代码也有自己的内存空间!这意味着你可以在主代码中有一个名为 test 的变量,在子程序中有一个名为 test 的变量,在另一个子程序中还有一个名为 test 的变量。请注意!这三个变量是完全不同的变量,位于不同的内存位置,它们可以存储完全不同的值。
正如你在下面的程序中可以看到的,有三个名为 test 的变量位于三个不同的内存位置,每个变量都持有完全不同的值。下面的跟踪表可以帮助你理解实际发生了什么。
file_36.1
def f1():
test = "Testing!"
print(test)
def f2(test):
print(test)
主代码从这里开始
test = 5
print(test)
f1()
f2(10)
print(test)
跟踪表如下所示。
| 步骤 | 语句 | 备注 | 主代码 | 无返回值函数 f1() | 无返回值函数 f2() |
|---|---|---|---|---|---|
| test | test | test | |||
| 1 | test = 5 | 5 | |||
| 2 | print(test) | 它显示:5 | 5 | ||
| 3 | f1() | f1()被调用 | ? | ||
| 4 | test = "Testing!" | Testing! | |||
| 5 | print(test) | 它显示:Testing! | Testing! | ||
| 6 | f2(10) | f2()被调用 | 10 | ||
| 7 | print(test) | 它显示:10 | 10 | ||
| 8 | print(test) | 它显示:5 | 5 |
请注意,在子程序中使用的变量“存在”的时间与子程序执行的时间一样长。这意味着在调用子程序之前,它的任何变量(包括形式参数列表中的变量)都不存在于主内存(RAM)中。它们都是在子程序被调用时在主内存中定义的,当子程序完成并且执行流程返回调用者时,它们都会从主内存中移除。唯一“永远存在”或至少在 Python 程序执行期间存在的变量是主代码的变量和全局变量!你将在第 36.6 节中了解更多关于全局变量的内容。
36.2 子程序能否调用另一个子程序?
到目前为止,你可能会有这样的印象,只有主代码可以调用子程序。然而,这并不正确!一个子程序可以调用任何其他子程序,而这个被调用的子程序又可以调用另一个子程序,以此类推。你可以创建任何你想要的组合。例如,你可以编写一个调用无返回值函数的函数,一个无返回值函数调用一个函数,一个函数调用另一个函数,甚至是一个调用 Python 内置函数的函数。
下一个示例正好展示了这种情况。主代码调用无返回值函数 displaySum(),然后它又调用函数 add()。
file_36.2
def add(number1, number2):
result = number1 + number2
return result
def displaySum(num1, num2):
打印(add(num1, num2))
Main code starts here
a = int(input())
b = int(input())
displaySum(a, b)
When the flow of execution reaches the return statement of the function add(), it returns to its caller, that is to the void function displaySum(). Then, when the flow of execution reaches the end of the void function displaySum(), it returns to its caller, that is, to the main code.
Note that there is no restriction on the order in which the two subprograms should be written. It would have been exactly the same if the void function displaySum() had been written before the function add().
36.3 通过值和引用传递参数
In Python, variables are passed to subprograms by value. This means that if the value of an argument is changed within the subprogram, it does not get changed outside of it. Take a look at the following example.
file_36.3a
def f1(b):
b += 1 #This is a variable of void function f1()
打印(b) #It displays: 11
Main code starts here
a = 10 #This is a variable of the main code
f1(a)
打印(a) #It displays: 10
The value 10 of variable a is passed to void function f1() through argument b. However, although the content of variable b is altered within the void function, when the flow of execution returns to the main code this change does not affect the value of variable a.
In the previous example, the main code and the void function are using two variables with different names. Yet, the same would have happened if, for instance, both the main code and the void function had used two variables of the same name. The next example operates exactly the same way and displays exactly the same results as the previous example did.
file_36.3b
def f1(a):
a += 1 #This is a variable of void function f1()
打印(a) #It displays: 11
Main code starts here
a = 10 #This is a variable of the main code
f1(a)
打印(a) #It displays: 10
Passing a list to a subprogram as an argument is as easy as passing a simple variable. The next example passes list a to the void function display(), and the latter displays the list.
file_36.3c
ELEMENTS = 10
def display(b):
for i in range(ELEMENTS):
打印(b[i], end = "\t")
Main code starts here
a = [None] * ELEMENTS
for i in range(ELEMENTS):
a[i] = int(input())
display(a)
Contrary to variables, data structures in Python are, by default, passed by reference. This means that if you pass, for example, a list to a subprogram, and that subprogram changes the value of one or more elements of the list, these changes are also reflected outside the subprogram. Take a look at the following example.
file_36.3d
def f1(x):
x[0] += 1
打印(x[0]) #It displays: 6
Main code starts here
y = [5, 10, 15, 20]
打印(y[0]) #It displays: 5
f1(y)
打印(y[0]) #It displays: 6
将列表传递给子程序传递的是列表的引用,而不是列表的副本,这意味着 y 和 x 实际上是同一列表的别名。主内存(RAM)中只存在列表的一个副本。如果一个子程序更改了一个元素的值,这种更改也会反映在主程序中。
因此,正如你可能已经意识到的,通过引用传递列表可以为子程序提供一个间接的方式“返回”多个值。然而,请记住,在 Python 中,使用列表从子程序返回多个值是非常不寻常的,因为你已经学过,Python 提供了更方便的方式来完成这个任务。但是,让我们看看,尽管是形式上的,一个这样的例子。在下一个示例中,函数 myDivmod()将变量 a 除以变量 b,并找到它们的整数商和整数余数。如果一切顺利,它返回 True;否则,它返回 False。此外,通过列表 results,函数还间接返回计算出的商和余数。
file_36.3e
def myDivmod(a, b, results):
returnValue = True
if b == 0:
returnValue = False
else:
results[0] = a // b
results[1] = a % b
return returnValue
主代码从这里开始
res = [None] * 2
val1 = int(input())
val2 = int(input())
ret = myDivmod(val1, val2, res)
if ret:
print(res[0], res[1])
else:
print("对不起,输入了错误的值!")
关于形式参数列表中的参数的一个很好的策略是将所有通过值传递的参数写在通过引用传递的参数之前。
36.4 返回列表
在下一个示例中,Python 程序必须找到列表 t 中的三个最小值。为此,程序调用并传递列表 t 到通过其形式参数 x 调用的 void 函数 getList(),该函数随后使用插入排序算法对列表 x 进行排序。当执行流程返回主代码时,列表 t 也被排序。这是因为,如前所述,Python 中的列表是通过引用传递的。因此,主代码最终所做的只是显示列表的前三个元素的值。
file_36.4a
ELEMENTS = 10
def getList( x ): [更多…]
for m in range(1, ELEMENTS):
element = x[m]
n = m
while n > 0 and x[n - 1] > element:
x[n] = x[n - 1]
n -= 1
x[n] = element
主代码从这里开始
t = [75, 73, 78, 70, 71, 74, 72, 69, 79, 77]
getList(t)
print("三个最小值是:", t[0], t[1], t[2])
在此步骤中,列表 t 被排序
for i in range(ELEMENTS):
print(t[i], end = "\t")
由于主代码中的列表 t 是通过引用传递给 void 函数的,因此在主内存(RAM)中只存在列表的一个副本,这意味着 t 和 x 实际上是同一列表的别名。当执行流程返回主代码时,列表 t 也被排序。
然而,很多时候通过引用传递列表可能会完全灾难性。假设您有两个列表。列表 names 包含 10 个城市的名称,列表 t 包含在特定一天特定时间记录的相应温度。

现在,假设您希望显示列表 t 的三个最低温度。如果您调用前一个 Python 程序中的 void 函数 getList(),您会遇到问题。尽管可以按要求显示三个最低温度,但列表 t 变得排序了;因此,其元素与列表 names 的元素之间的一一对应关系永远丢失了!
一种可能的解决方案是编写一个函数,其中将列表复制到一个辅助列表中,该函数将返回一个只包含三个最低值的较小列表。提出的解决方案如下所示。
file_36.4b
ELEMENTS = 10
def getList(x):
将列表 x 复制到列表 auxX
auxX = [None] * ELEMENTS
for m in range(ELEMENTS):
auxX[m] = x[m]
并对列表 auxX 进行排序
for m in range(1, ELEMENTS):
element = auxX[m]
n = m
while n > 0 and auxX[n - 1] > element:
auxX[n] = auxX[n - 1]
n -= 1
auxX[n] = element
return auxX[:3] #使用切片返回列表的前 3 个元素作为列表
主代码从这里开始
names = ["City1", "City2", "City3", "City4", "City5", \
"City6", "City7", "City8", "City9", "City10"]
t = [75, 73, 78, 70, 71, 74, 72, 69, 79, 77]
low = getList(t)
print("三个最低值是: ", low[0], low[1], low[2])
在这一步,列表 t 未排序
for i in range(ELEMENTS):
print(t[i], "\t", names[i])
请注意,您不能使用类似于 auxX = x 这样的语句来复制列表 x 的元素到 auxX。这个语句只是创建了同一列表的两个别名。这就是为什么在前一个例子中使用 for 循环将列表 x 的元素复制到列表 auxX 的原因。
将一个列表的所有元素复制到另一个列表的更 Pythonic 的方法是使用切片机制。在前面的例子中,您可以使用语句 auxX = x[:] 来这样做。
这里展示了另一种更 Pythonic 的方法。
file_36.4c
def getList(x):
return sorted(x)[:3] #只返回排序列表的前 3 个元素
主代码从这里开始
names = ["City1", "City2", "City3", "City4", "City5", \
"City6", "City7", "City8", "City9", "City10"]
t = [75, 73, 78, 70, 71, 74, 72, 69, 79, 77]
low = getList(t)
print("三个最低值是: ", low[0], low[1], low[2])
在这一步,列表 t 未排序
for i in range(len(t)):
print(t[i], "\t", names[i])
函数 sorted(x) 返回一个新的排序列表,同时保持初始列表 x 完好无损(见第 32.7 节)。
36.5 默认参数值(可选参数)和关键字参数
如果在形式参数列表中为参数分配默认值,这意味着如果没有为该参数传递值,则使用默认值。在下一个例子中,函数 prependTitle()被设计为在名称之前添加(添加前缀)一个头衔。然而,如果没有为参数 title 传递值,则函数使用默认值“M”。
file_36.5a
def prependTitle(name, title = "M"):
return title + " " + name
主代码从这里开始
print(prependTitle("John King")) #它显示:M John King
print(prependTitle("Maria Miller", "Ms")) #它显示:Ms Maria Miller
当在形式参数列表中为参数分配默认值时,该参数被称为“可选参数”。
在形式参数列表中,任何可选参数都必须位于任何非可选参数的右侧;否则,这种做法是不正确的。
此外,在 Python 中,可以使用关键字参数以如下形式调用子程序:
argument_name = value
Python 假设关键字参数是可选的。如果在子程序调用中未提供任何参数,则使用默认值。看看下面的 Python 程序。函数 prependTitle()被调用了四次。然而,在最后一次调用中,使用了关键字参数。
file_36.5b
def prependTitle(firstName, lastName, title = "M", reverse = False):
if not reverse:
returnValue = title + " " + firstName + " " + lastName
else:
returnValue = title + " " + lastName + " " + firstName
return returnValue
主代码从这里开始
print(prependTitle("John", "King")) #它显示:M John King
print(prependTitle("Maria", "Miller", "Ms")) #它显示:Ms Maria Miller
print(prependTitle("Maria", "Miller", "Ms", True)) #它显示:Ms Miller Maria
使用关键字参数调用函数
print(prependTitle("John", "King", reverse = True)) #它显示:M King John
请注意,参数 reverse 在形式参数列表中的顺序是第四位。但是,使用关键字参数可以绕过这个顺序。
与使用“关键字参数”这个术语不同,许多计算机语言,如 PHP、C#和 Visual Basic(仅举几个例子),更倾向于使用“命名参数”这个术语。
36.6 变量的作用域
变量的作用域指的是该变量的作用范围。在 Python 中,一个变量可以具有局部或全局作用域。在子程序内部声明的变量具有局部作用域,并且只能从该子程序内部访问。另一方面,在子程序外部声明的变量具有全局作用域,可以从任何子程序以及主代码中访问。
让我们看看一些例子。下一个例子声明了一个全局变量 test。然而,这个全局变量的值是在 void 函数内部访问和显示的。
file_36.6a
def displayValue():
print(test) #It displays: 10
Main code starts here
test = 10 #This is a global variable
displayValue()
print(test) #It displays: 10
现在的问题是,“如果你尝试在函数 displayValue()中更改变量 test 的值,会发生什么?这会影响全局变量 test 吗?”在下一个示例中,将显示值 20 和 10。
file_36.6b
def displayValue():
test = 20 #This is a local variable
print(test) #It displays: 20
Main code starts here
test = 10 #This is a global variable
displayValue()
print(test) #It displays: 10
This happens because Python declares two variables in main memory (RAM); that is, a global variable test and a local variable test.
Now let's combine the first example with the second one and see what happens. First the subprogram will access the variable test, and then it will assign a value to it, as shown in the code that follows.
file_36.6c
def displayValue():
print(test) #This statement throws an error
test = 20
print(test)
Main code starts here
test = 10 #This is a global variable
displayValue()
print(test)
Unfortunately, this example throws the error message “local variable 'test' referenced before assignment”. This happens because Python “assumes” that you want a local variable due to the assignment statement test = 20 within function displayValue(). Therefore, the first print() statement inevitably throws this error message. Any variable that is defined or altered within a function is automatically declared as local, unless it has been forced to be a global variable. To force Python to use the global variable you have to use the keyword global, as you can see in the following example.
file_36.6d
def displayValue():
global test #Force Python to use the global variable test
print(test) #It displays: 10
test = 20
print(test) #It displays: 20
Main code starts here
test = 10 #This is a global variable
displayValue()
print(test) #It displays: 20
如果你希望在子程序中访问多个全局变量,你可以写下关键字 global,然后在其旁边,用逗号分隔所有变量,如下所示。
global a, b, c
If the value of a global variable is altered within a subprogram, this change is also reflected outside of the subprogram. Please note that the last print(test) statement of the main code displays the value of 20.
Any variable that is defined or altered within a function is local unless it is declared as a global variable using the keyword global.
下一个程序声明了一个全局变量 test,一个在无返回值函数 displayValueA()内的本地变量 test,以及另一个在无返回值函数 displayValueB()内的本地变量 test。请记住,全局变量 test 和这两个本地变量 test 是三个不同的变量!此外,第三个无返回值函数 displayValueC()使用并改变了全局变量 test 的值。
file_36.6e
def displayValueA():
test = 7 # 本地变量 test
print(test) # 显示:7
def displayValueB():
test = 9 # 本地变量 test
print(test) # 显示:9
def displayValueC():
global test # 使用全局变量 test 的值
print(test) # 显示:10
test += 1 # 增加全局变量 test 的值
主代码从这里开始
test = 10 # 这是全局变量 test
print(test) # 显示:10
displayValueA()
print(test) # 显示:10
displayValueB()
print(test) # 显示:10
displayValueC()
print(test) # 显示:11
你可以在不同的子程序中拥有同名的局部作用域变量,因为它们只在其声明的子程序中被识别。
36.7 将代码部分转换为子程序
将大型程序不分割成更小的子程序会导致难以理解和维护的代码。假设你有一个大型程序,并且希望将其分割成更小的子程序。下一个程序是一个示例,解释必须遵循的步骤。程序中用虚线矩形标记的部分必须转换为子程序。
file_36.7a
totalYes = 0
femaleNo = 0
for i in range(100):
while True:
temp1 = input("为公民编号" + str(i + 1) + "输入性别:")
gender = temp1.lower()
if gender in ["male", "female", "other"]: break
while True:
temp2 = input("你下午去慢跑吗?")
answer = temp2.lower()
if answer in ["yes", "no", "sometimes"]: break
if answer == "yes":
totalYes += 1
if gender == "female" and answer == "no":
femaleNo += 1
print("总肯定回答数:", totalYes)
print("女性的否定回答数:", femaleNo)
要将此程序的部分转换为子程序,你必须:
►对于每个虚线矩形,决定是否使用函数或无返回值函数。这取决于子程序是否会返回结果。
►确定每个虚线矩形中存在的变量及其在该虚线矩形中的作用。
下面的流程图可以帮助你决定每个变量的处理方式,无论是必须传递给子程序和/或从子程序返回,还是只需在子程序内部作为本地变量。

请记住,Python 中的函数可以返回多个结果!
因此,借助这个流程图,让我们逐个处理每个虚线矩形!未标记虚线矩形的部分将构成主代码。
第一部分
在第一个虚线矩形中,有三个变量:i、temp1 和 gender。然而,并非所有这些都必须包含在替换虚线矩形的子程序的形式参数列表中。让我们找出原因!
►变量 i:
►在虚线矩形外初始化/更新;因此,必须将其传递给子程序
►在虚线矩形内未更新;因此,不应将其返回给调用者
►变量 temp1:
►在虚线矩形外未初始化/更新;因此,不应将其传递给子程序
►在虚线矩形内初始化,但其值在矩形外未使用;因此,不应将其返回给调用者
根据流程图,由于变量 temp1 既不应传递也不应返回,因此这个变量可以是子程序内的局部变量。
►变量 gender:
►在虚线矩形外未初始化/更新;因此,不应将其传递给子程序
►在虚线矩形内初始化,然后其值在矩形外使用;因此,必须将其返回给调用者
因此,由于必须将一个值返回给主代码,可以使用如下所示的功能。
第一部分
def getGender(i):
while True:
temp1 = input("请输入公民编号" + str(i + 1) + "的性别:")
gender = temp1.lower()
if gender in ["male", "female", "other"]: break
return gender
第二部分
在第二个虚线矩形中有两个变量,temp2 和 answer,但它们并不都需要包含在替换虚线矩形的子程序的形式参数列表中。让我们找出原因!
►变量 temp2:
►在虚线矩形外未初始化/更新;因此,不应将其传递给子程序
►在虚线矩形内初始化/更新,但其值在矩形外未使用;因此,不应将其返回给调用者
根据流程图,由于变量 temp2 既不应传递也不应返回,因此这个变量可以是子程序内的局部变量。
►变量 answer:
►在虚线矩形外未初始化/更新;因此,不应将其传递给子程序
►在虚线矩形内初始化,然后其值在矩形外使用;因此,必须将其返回给调用者
因此,由于必须将一个值返回给主代码,可以使用功能,如下所示。
第二部分
def getAnswer():
while True:
temp2 = input("你下午去慢跑吗? ")
answer = temp2.lower()
if answer in ["yes", "no", "sometimes"]: break
return answer
第三部分
在示例的第三个虚线矩形中,有四个变量:answer、totalYes、gender 和 femaleNo,它们都必须包含在替换虚线矩形的子程序的正式参数列表中。让我们找出原因!
►两个变量 answer 和 gender:
►在虚线矩形外部初始化/更新;因此,它们必须传递给子程序
►在虚线矩形内部没有更新;因此,它们不应该返回给调用者
►两个变量 totalYes 和 femaleNo:
►在虚线矩形外部初始化;因此,它们必须传递给子程序
►在虚线矩形内部更新并随后在矩形外部使用其值;因此,它们必须返回给调用者
因此,由于必须将两个值返回到主代码,可以使用如下所示的功能。
第三部分
def countResults(answer, gender, totalYes, femaleNo):
if answer == "yes":
totalYes += 1
if gender == "female" and answer == "no":
femaleNo += 1
return totalYes, femaleNo
第四部分
在示例的第四个虚线矩形中,有两个变量:totalYes 和 femaleNo。让我们看看你应该如何处理它们。
►两个变量 totalYes 和 femaleNo:
►在虚线矩形外部更新;因此,它们必须传递给子程序
►在虚线矩形内部没有更新;因此,它们不应该返回给调用者
因此,由于不应将任何值返回到主代码,可以使用如下所示的 void 函数。
第四部分
def displayResults(totalYes, femaleNo):
print("总肯定回答数:", totalYes)
print("女性的否定回答数:", femaleNo)
最后的程序
包含主代码和上述所有子程序的最后程序如下所示。
file_36.7b
第一部分
def getGender(i):
while True:
temp1 = input("为公民 No " + str(i + 1) + " 输入性别:")
gender = temp1.lower()
if gender in ["male", "female", "other"]: break
return gender
第二部分
def getAnswer():
while True:
temp2 = input("你下午去慢跑吗?")
answer = temp2.lower()
if answer in ["yes", "no", "sometimes"]: break
return answer
第三部分
def countResults(answer, gender, totalYes, femaleNo):
if answer == "yes":
totalYes += 1
if gender == "female" and answer == "no":
femaleNo += 1
return totalYes, femaleNo
第四部分
def displayResults(totalYes, femaleNo):
print("总肯定回答数:", totalYes)
print("女性的否定回答数:", femaleNo)
主代码从这里开始
totalYes = 0
femaleNo = 0
for i in range(100):
gender = getGender(i)
answer = getAnswer()
totalYes, femaleNo = countResults(answer, gender, totalYes, femaleNo)
displayResults(totalYes, femaleNo)
36.8 递归
递归是一种编程技术,其中子程序调用自身。这最初可能看起来像是一个无限循环,但当然这不是真的;使用递归的子程序必须以明显满足有限性属性的方式编写。
想象一下下一个 Python 程序能帮助你找到回家的路。在这个程序中,由于 void 函数 find_your_way_home()在函数内部调用自身,因此发生了递归。
def find_your_way_home():
if you_are_already_at_home:
stop_walking()
else:
take_one_step_toward_home()
find_your_way_home()
主代码从这里开始
find_your_way_home()
现在,让我们通过一个真实示例来尝试分析递归。下一个 Python 程序使用递归计算 5 的阶乘。
file_36.8
def factorial(value):
if value == 1:
return 1
else:
return factorial(value - 1) * value
主代码从这里开始
print(factorial(5)) #它显示:120
在数学中,非负整数 N 的阶乘是所有小于或等于 N 的正整数的乘积。它用 N!表示,0 的阶乘按定义等于 1。例如,5 的阶乘是 1 × 2 × 3 × 4 × 5 = 120。
递归发生是因为 factorial()函数在函数内部调用自身。
请注意,这里没有循环控制结构!
你现在可能感到困惑。没有使用循环控制结构,如何计算 1 × 2 × 3 × 4 × 5 的乘积呢?下一个图可能有助于你理解。它显示了当 factorial(5)函数通过一系列调用向后工作时执行的乘法操作。

让我们看看这个图是如何工作的。主代码调用 factorial(5)函数,该函数反过来调用 factorial(4)函数,然后 factorial(4)调用 factorial(3),依此类推。最后一个调用(factorial(1))将其值 1 返回给调用者(factorial(2)),然后 factorial(2)将其值 1 × 2 = 2 返回给调用者(factorial(3)),依此类推。当 factorial(5)从最顶层的调用返回时,你就有了解决方案。
为了避免逻辑错误,所有递归子程序都必须遵循三个重要的规则:
1)它们必须调用自己。
2)它们必须有一个基本情况,这是告诉子程序停止递归的条件。基本情况通常是一个非常小的问题,可以直接解决。它是“最简单”可能问题的解决方案。在上一个示例中的 factorial()函数中,基本情况是 1 的阶乘。当调用 factorial(1)时,布尔表达式 value == 1 计算为 True,并标志着递归的结束。
3)它们必须改变它们的状态,并向基本情况移动。状态改变意味着子程序改变了一些数据。通常,数据以某种方式逐渐减小。在上一个示例中的 factorial()函数中,因为基本情况是 1 的阶乘,整个概念依赖于向那个基本情况移动的想法。
总之,递归有助于你编写更富有创意和更优雅的程序,但请记住,它并不总是最佳选择。递归的主要缺点是程序员难以理解其逻辑,因此包含递归子程序的代码难以调试。此外,递归算法可能比非递归算法更差,因为它可能消耗过多的 CPU 时间或过多的主内存(RAM)。因此,有时遵循 KISS 原则会更好,而不是使用递归,而是使用循环控制结构来解决算法。
对于不知道 KISS 原则的人来说,它是一个缩写,代表“Keep It Simple, Stupid”!它表明,大多数系统如果保持简单,则工作得最好,避免任何不必要的复杂性!
36.9 复习问题:正确/错误
对于以下每个陈述,选择正确或错误。
1)每个子程序都使用自己的内存空间来存储其变量的值。
2)在子程序中使用的变量只要子程序正在执行就会“存活”。
3)唯一在 Python 程序执行期间“存活”的变量是主代码的变量和全局变量。
4)子程序可以调用主代码。
5)如果通过值传递一个参数并在子程序中更改其值,则该值在外部不会改变。
6)实际参数的名称必须与相应的形式参数的名称相同。
7)实际参数的总数不能超过形式参数的总数。
8)表达式不能传递给子程序。
9)默认情况下,Python 中的列表是通过引用传递的。
10)你可以将列表传递给 void 函数,但 void 函数不能(直接或间接地)将列表返回给调用者。
11)函数可以通过其形式参数列表接受一个列表。
12)通常,void 函数可以调用任何函数。
13)通常,一个函数可以调用任何 void 函数。
14)在语句内部,一个函数只能调用一次。
15)void 函数可以通过其形式参数列表返回一个值。
16)子程序可以被另一个子程序或主代码调用。
17)如果你在形式参数列表中为参数分配了一个默认值,这意味着无论传递给该参数的值是什么,都将使用默认值。
18)当一个参数在实参列表中分配了默认值时,该参数被称为可选参数。
19)可选参数必须位于任何非可选参数的左侧。
20)参数的默认值不能是字符串。
21)变量的作用域指的是该变量的作用范围。
22)如果在子程序中更改了全局变量的值,这种更改也会在子程序外部反映出来。
23)你可以有两个同名的全局变量。
24)递归是一种编程技术,其中子程序会调用自身。
25)递归算法必须有一个基本情况。
26)使用递归解决问题并不总是最佳选择。
36.10 复习练习
完成以下练习。
1)在不使用跟踪表的情况下,你能找出下一个 Python 程序显示的内容吗?
def f1():
a = 22
def f2():
a = 33
a = 5
f1()
f2()
print(a)
2)在不使用跟踪表的情况下,你能找出下一个 Python 程序显示的内容吗?
def f1(number1):
return 2 * number1
def f2(number1, number2):
return f1(number1) + f1(number2)
a = 3
b = 4
print(f2(a, b))
3)在不使用跟踪表的情况下,你能找出下一个 Python 程序显示的内容吗?
def f1(number1):
return number1 * 2
def f2(number1, number2):
number1 = f1(number1)
number2 = f1(number2)
return number1 + number2
a = 2
b = 5
print(f2(a, b))
4)在不使用跟踪表的情况下,你能找出下一个 Python 程序显示的内容吗?
def display(s = "hello"):
s = s.replace("a", "e")
print(s, end = "")
display("hello")
display()
display("hallo")
5)在不使用跟踪表的情况下,你能找出下一个 Python 程序显示的内容吗?
def f1():
global a
a = a + b
a = 10
b = 5
f1()
b -= 1
print(a)
6)在不使用跟踪表的情况下,你能找出下一个 Python 程序显示的内容吗?
def f2():
global a
a = a + b
def f1():
global a
a = a + b
f2()
a = 3
b = 4
f1()
print(a, b)
7)在不使用跟踪表的情况下,你能找出下一个 Python 程序显示的内容吗?
def foo(a, b):
c = 0
for x in a:
if x == b:
c += 1
return c
print(foo([5, 9, 2, 5, 5], 5))
8)以下 Python 程序旨在提示用户将五个整数输入到列表中,然后显示每个元素的位数和整数本身。例如,如果用户输入的值是 35、13565、113、278955、9999,则程序应显示:
数字 35 中有 2 位
数字 13565 中有 5 位
3 digits in number 113
数字 278955 中有 6 位
4 digits in number 9999
不幸的是,程序显示
数字 0 中有 2 位
数字 0 中有 5 位
数字 0 中有 3 位
数字 0 中有 6 位
数字 0 中有 4 位
你能找出原因吗?
ELEMENTS = 5
def getNumOfDigits(x, index):
count = 0
while x[index] != 0:
count += 1
x[index] = x[index] // 10
return count
主代码从这里开始
val = [None] * ELEMENTS
for i in range(ELEMENTS):
val[i] = int(input())
for i in range(ELEMENTS):
print(getNumOfDigits(val, i), "digits in number", val[i])
9)对于以下 Python 程序,将标记为虚线矩形的部分转换为子程序。
STUDENTS = 10
LESSONS = 5
names = [None] * STUDENTS
grades = [[None] * LESSONS for i in range(STUDENTS)]
for i in range(STUDENTS):
names[i] = input("Enter name No. " + str(i + 1) + ": ")
for j in range(LESSONS):
grades[i][j] = int(input("Enter grade for lesson No. " + str(j + 1) + ": "))
average = [None] * STUDENTS
for i in range(STUDENTS):
average[i] = 0
for j in range(LESSONS):
average[i] += grades[i][j]
average[i] /= LESSONS
for m in range(1, STUDENTS):
for n in range(STUDENTS - 1, m - 1, -1):
if average[n] > average[n - 1]:
average[n], average[n - 1] = average[n - 1], average[n]
names[n], names[n - 1] = names[n - 1], names[n]
elif average[n] == average[n - 1]:
if names[n] < names[n - 1]:
names[n], names[n - 1] = names[n - 1], names[n]
for i in range(STUDENTS):
print(names[i], "\t", average[i])
10)对于以下 Python 程序,将带有虚线矩形标记的部分转换为子程序。
message = input("Enter a message: ").lower()
messageClean = ""
for i in range(len(message)):
if message[i] not in " ,.?":
messageClean += message[i]
middlePos = (len(messageClean) - 1) // 2
j = len(messageClean) - 1
palindrome = True
for i in range(middlePos + 1):
if messageClean[i] != messageClean[j]:
palindrome = False
break
j -= 1
if palindrome:
print("The message is palindrome")
11)下一个 Python 程序在四个用户提供的值中找到最大值。重写该程序,不使用子程序。
def myMax(n, m):
if n > m:
m = n
return m
a = int(input())
b = int(input())
c = int(input())
d = int(input())
maximum = a
maximum = myMax(b, maximum)
maximum = myMax(d, maximum)
maximum = myMax(c, maximum)
print(maximum)
12)编写两个子程序,一个函数和一个 void 函数。它们都必须通过其形式参数列表接受三个数字,然后返回它们的总和和平均值。
13)编写一个名为 myRound 的子程序,通过其形式参数列表接受一个实数(浮点数)和一个整数,然后返回实数四舍五入到整数指示的小数位数。此外,如果未传递整数值,则子程序必须默认将实数四舍五入到两位小数。尽量不使用 Python 的 round()函数。
14)执行以下操作:
i)编写一个名为 getInput 的子程序,提示用户输入“是”或“否”的答案,然后相应地返回 True 或 False 给调用者。该子程序应接受所有可能的答案形式,如“是”、“YES”、“Yes”、“No”、“NO”、“nO”等。
ii)编写一个名为 findArea 的子程序,通过其形式参数列表接受平行四边形的底和高,然后返回其面积。
iii)使用上述子程序,编写一个 Python 程序,提示用户输入平行四边形的底和高,然后计算并显示其面积。程序必须根据用户的意愿迭代多次。每次计算结束后,程序必须询问用户是否希望计算另一个平行四边形的面积。如果答案是“是”,则程序必须重复。
15)执行以下操作:
i)编写一个名为 getLists 的子程序,提示用户输入 100 名学生的成绩和姓名,分别存储在 grades 和 names 列表中。这两个列表必须返回给调用者。
ii)编写一个名为 getAverage 的子程序,通过其形式参数列表接受成绩列表 grades,并返回平均成绩。
iii)编写一个名为 sortLists 的子程序,该子程序通过其形式参数列表接受成绩和姓名列表,并使用插入排序算法按降序对成绩列表进行排序。该子程序必须保持两个列表元素的一对一对应关系。
iv)使用上述子程序,编写一个 Python 程序,提示用户输入 100 名学生的成绩和姓名,然后按成绩降序显示所有成绩低于平均成绩的学生姓名。
16)在一场歌唱比赛中,有一位艺术家由 10 位评委评分。然而,根据该比赛的规则,总分是在排除最高分和最低分之后计算的。执行以下操作:
i)编写一个名为 getList 的子程序,该子程序提示用户输入 10 位评委的分数到一个列表中,然后返回该列表给调用者。假设每个分数都是唯一的。
ii)编写一个名为 findMinMax 的子程序,该子程序通过其形式参数列表接受一个列表,然后返回最大值和最小值。
iii)使用上述子程序,编写一个 Python 程序,提示用户输入艺术家的姓名和每位评委给出的分数。然后程序必须显示消息“艺术家 NN 获得了 XX 分”,其中 NN 和 XX 必须替换为实际值。
17)执行以下操作:
i)编写一个名为 sumRecursive 的递归函数,该函数通过其形式参数列表接受一个整数,然后返回从 1 到该整数的数字之和。
ii)使用上述子程序,编写一个 Python 程序,允许用户输入一个正整数,然后显示从 1 到该用户提供的整数的所有数字之和。
18)在棋盘上,你必须在每个方格上放置麦粒,第一个方格放一粒,第二个方格放两粒,第三个方格放四粒,依此类推(每个后续方格上的麦粒数是前一个方格的两倍)。执行以下操作:
i)编写一个名为 woc 的递归函数,该函数接受一个方格的索引并返回该方格上的麦粒数量。由于棋盘包含 8 × 8 = 64 个方格,假设索引是一个介于 1 和 64 之间的整数。
ii)使用上述子程序,编写一个 Python 程序,计算并显示棋盘上最终的总麦粒数。
19)斐波那契数列是一系列数字,其顺序如下:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, …
根据定义,前两项是 0 和 1,每一项后续都是前两项之和。
执行以下操作:
i)编写一个名为 fib 的递归函数,该函数通过其形式参数列表接受一个整数,然后返回斐波那契数列的第 N 项。
ii)使用上述子程序,编写一个 Python 程序,允许用户输入一个正整数 N,然后显示斐波那契数列的第 N 项。
20)三进制数列与斐波那契数列类似,但每个项是前三个项的和。编写一个名为 trib 的递归函数,该函数通过其形式参数列表接受一个整数,然后返回三进制数列的第 N 项。
21)编写一个名为 myPow 的递归函数,该函数接受一个实数和一个整数,然后返回第一个数以第二个数为指数的结果,不使用指数运算符(**)或甚至 Python 的内置 pow() 函数。确保该函数对于正指数值和负指数值都能正确工作。
22)执行以下操作:
i)编写一个名为 factorial 的递归函数,该函数通过其形式参数列表接受一个整数,然后返回其阶乘。
ii)使用上述引用的函数,编写一个名为 myCos 的递归函数,该函数使用泰勒级数计算并返回 x 的余弦值,如下所示。
.
提示:请记住 x 是以弧度为单位的,
。
iii)使用上述引用的函数 myCos(),编写一个 Python 程序,计算并显示 45^o 的余弦值。
提示:为了验证结果,请注意 45^o 的余弦值大约为 0.7071067811865475。
第三十七章
更多关于子程序的内容
37.1 使用子程序进行简单练习
练习 37.1-1 简单货币转换器
执行以下操作:
i)编写一个名为 displayMenu 的子程序,显示以下菜单。
1)将美元 (USD) 转换为欧元 (EUR)
2)将欧元 (EUR) 转换为美元
3)退出
ii)使用上述子程序,编写一个 Python 程序,显示之前提到的菜单并提示用户输入选择(1、2 或 3)。如果选择 1 或 2,程序必须提示用户输入金额,然后必须计算并显示相应的转换值。此过程必须根据用户的意愿重复进行。
已知 1 美元 = 0.94 欧元 (€)。
解决方案
根据“终极”规则,主代码中的 while 循环必须如下,以一般形式给出。
displayMenu();
choice = int(input()) #choice 的初始化
while choice != 3:
提示用户输入金额,然后计算并显示所需的价值。
displayMenu();
choice = int(input()) #更新/修改选择
解决方案如下。
file_37.1-1
def displayMenu():
print("----------------------------")
print("1. 将美元 (USD) 转换为欧元 (EUR)")
print("2. 将欧元 (EUR) 转换为美元")
print("3. 退出")
print("----------------------------")
print("输入选择: ", end = "")
主代码从这里开始
displayMenu()
choice = int(input())
while choice != 3:
amount = float(input("输入金额: "))
if choice == 1:
print(amount, "USD =", amount * 0.94, "Euro")
else:
print(amount, "Euro =", amount / 0.94, "USD")
displayMenu()
choice = int(input())
练习 37.1-2 查找正整数的平均值
执行以下操作:
i)编写一个名为 testInteger 的子程序,它通过其形式参数列表接受一个数字,当传递的数字是整数时返回 True;否则返回 False。
ii)使用上述子程序,编写一个 Python 程序,允许用户重复输入数值,直到输入一个实数。最后,程序必须显示输入的正整数的平均值。
解决方案
为了解决这个练习,将使用 while 语句。根据第 28.3 节中讨论的“终极”规则,解决此问题的预测试循环结构应如下所示。
x = float(input()) #x 的初始化
while testInteger(x): #依赖于 x 的布尔表达式
一个语句或语句块
x = float(input()) #x 的更新/修改
语句 while testInteger(x) 等价于语句 while testInteger(x) == True。
最终解决方案将在下面展示。
file_37.1-2
def testInteger(number):
returnValue = False
if number == int(number):
returnValue = True
return returnValue
主代码从这里开始
total = 0
count = 0
x = float(input()) #x 的初始化
while testInteger(x): #依赖于 x 的布尔表达式
if x > 0:
total += x
count += 1
x = float(input()) #更新/修改 x
if count > 0:
print(总和 / count)
注意最后的单分支决策结构,if count > 0. 它对于程序满足确定性属性是必要的。想想看!如果用户一开始就输入一个实数,那么变量 count,最终将包含零的值。
以下函数可以作为之前函数的替代。它直接返回布尔表达式数字等于 int(数字)的结果(True 或 False)。
def test_integer(数字):
return 数字等于 int(数字)
练习 37.1-3 求奇数正整数的和
执行以下操作:
i)编写一个名为 testInteger 的子程序,它通过其形式参数列表接受一个数字,当传入的数字是整数时返回 True;否则返回 False。
ii)编写一个名为 testOdd 的子程序,它通过其形式参数列表接受一个数字,当传入的数字是奇数时返回 True;否则返回 False。
iii)编写一个名为 testPositive 的子程序,它通过其形式参数列表接受一个数字,当传入的数字是正数时返回 True;否则返回 False。
iv)使用上述子程序,编写一个 Python 程序,允许用户重复输入数值,直到输入一个负数。最后,程序必须显示输入的奇数正整数的总和。
解决方案
这个练习基本上与上一个相同。每个子程序返回一个值(可以是 True 或 False)。解决方案在此处展示。
file_37.1-3
def test_integer(数字):
return 数字等于 int(数字)
def testOdd(数字):
return 数字 % 2 != 0
def testPositive(数字):
return 数字 > 0
主代码从这里开始
总和 = 0
x = float(input())
while testPositive(x):
if testInteger(x) and testOdd(x):
总和 += x
x = float(input())
print(总和)
语句 if testInteger(x) and testOdd(x) 等价于语句 if testInteger(x) == True and testOdd(x) == True
练习 37.1-4 求解 y 的值
编写一个 Python 程序,找出并显示以下公式中 y 的值(如果可能)。
.
对于公式的每一部分,编写一个子程序,它通过其形式参数列表接受 x,然后计算并显示结果。当计算不可能时,子程序必须显示错误信息。
解决方案
每个子程序必须计算并显示相应公式的结果,或者在计算不可能时显示错误信息。由于这两个子程序不返回结果,它们都可以写成 void 函数。解决方案在此处展示。
file_37.1-4
def formula1(x):
if x == 5: #当 x >= 1 时,无需检查 x == 0
print("错误!除以零")
else:
y = 3 * x / (x - 5) + (7 - x) / (2 * x)
print(y)
def formula2(x):
if x == -2:
print("错误!除以零")
else:
y = (45 - x) / (x + 2) + 3 * x
print(y)
x = float(input("输入 x 的值: "))
i)编写一个名为 getAge 的子程序,提示用户输入他们的年龄并返回它。使用循环控制结构,子程序还必须验证数据输入,并在用户输入任何非正值时显示错误消息。
if x >= 1:
formula1(x)
else:
formula2(x)
37.2 具有子程序的一般性质练习
练习 37.2-1 使用子程序验证数据输入
练习 37.2-2 使用子程序对列表进行排序
执行以下操作:
ii)编写一个名为 findMax 的子程序,它通过形式参数列表接受一个列表,并返回列表中最大值的索引位置。
iii)使用上述子程序编写一个 Python 程序,提示用户输入 50 人的名字、姓氏和年龄到三个列表中,然后显示最年长的人的名字。
解决方案
由于子程序 getAge()返回一个值,它可以写成函数。同样,对于也返回一个值的 findMax()子程序也是如此。主代码必须提示用户首先将 50 人的名字、姓氏和年龄分别输入到 firstNames、lastNames 和 ages 列表中。然后,借助 findMax()函数,它可以找到 ages 列表中最大值的索引位置。解决方案如下。
file_37.2-1
PEOPLE = 50
def getAge():
age = int(input("输入年龄: "))
while age <= 0:
print("错误:无效的年龄!")
age = int(input("输入一个正数: "))
return age
def findMax(a):
maximum = a[0]
maxI = 0
for i in range(1, PEOPLE):
if a[i] > maximum:
maximum = a[i]
maxI = i
return maxI
主代码从这里开始
firstNames = [None] * PEOPLE
lastNames = [None] * PEOPLE
ages = [None] * PEOPLE
for i in range(PEOPLE):
firstNames[i] = input("第" + str(i + 1) + "个人的名字: ")
lastNames[i] = input("第" + str(i + 1) + "个人的姓氏: ")
ages[i] = getAge()
indexOfMax = findMax(ages)
print("最年长的人是:", firstNames[indexOfMax], lastNames[indexOfMax])
print("他们", ages[indexOfMax], "岁!")
#主代码从这里开始
执行以下操作:
i)编写一个名为 mySwap 的子程序,它通过形式参数列表接受一个列表,以及两个索引。然后,子程序交换相应索引位置的元素值。
ii)使用上面提到的 mySwap()子程序,编写一个名为 mySort 的子程序,它通过形式参数列表接受一个列表,然后使用冒泡排序算法对列表进行排序。它必须能够按升序或降序排序。为此,在形式参数列表中包含一个额外的布尔参数。
iii)编写一个名为 displayList 的子程序,它通过形式参数列表接受一个列表,然后显示它。
iv)使用上面引用的子程序 mySort() 和 displayList(),编写一个 Python 程序,提示用户输入 20 个人的名字,然后显示两次:一次按升序排序,一次按降序排序。
解答
如您在下面的 Python 程序中可以看到,void 函数 mySort() 使用了冒泡排序算法的改进版本。当将值 True 传递给参数 ascending 时,算法按升序对列表 a 进行排序。当传递值 False 时,算法按降序对列表 a 进行排序。
此外,void 函数 mySort() 在需要交换两个元素的内容时,每次都会调用 void 函数 mySwap()。
file_37.2-2
PEOPLE = 20
def mySwap(a, index1, index2):
a[index1], a[index2] = a[index2] = a[index1]
def mySort(a, ascending = True):
for m in range(PEOPLE - 1):
for n in range(PEOPLE - 1, m, -1):
if ascending:
if a[n] < a[n - 1]:
mySwap(a, n, n - 1)
else:
if a[n] > a[n - 1]:
mySwap(a, n, n - 1)
def displayList(a):
for i in range(PEOPLE):
print(a[i])
主代码从这里开始
names = [None] * PEOPLE
for i in range(PEOPLE):
names[i] = input("Enter a name: ")
mySort(names) #按升序排序 names
displayList(names) #并显示它们
mySort(names, False) #按降序排序 names
displayList(names) #并显示它们。
注意,参数 ascending 是一个可选参数。这意味着如果没有为该参数传递值,则使用默认值 True。
在 Python 中,列表是通过引用传递的。这就是为什么在子程序 mySwap() 和 mySort() 中不需要包含返回语句。
练习 37.2-3 递进费率和电力消耗
LAV 电力公司根据以下表格(家庭账户的月度费率)向用户收取电力消耗费用。
| 千瓦时 (kWh) | 每千瓦时美元 |
|---|---|
| kWh ≤ 400 | $0.08 |
| 401 ≤ kWh ≤ 1500 | $0.22 |
| 1501 ≤ kWh ≤ 2000 | $0.35 |
| 2001 ≤ kWh | $0.50 |
执行以下操作:
i)编写一个名为 getConsumption 的子程序,该子程序提示用户输入消耗的总千瓦时数,然后返回它。使用循环控制结构,该子程序还必须验证数据输入,并在用户输入任何负值时显示错误消息。
ii)编写一个名为 findAmount 的子程序,该子程序通过其形式参数列表接受消耗的 kWh,然后返回应付的总金额(根据上面的表格)。
iii)使用上述子程序编写一个 Python 程序,提示用户输入消耗的总千瓦时数,然后显示应付的总金额。程序必须根据用户的意愿进行迭代。每次计算结束后,程序必须询问用户是否希望为另一个消费者计算总金额。如果答案是“是”,程序必须重复;否则必须结束。程序必须接受所有可能的形式的答案,如“是”、“YES”、“Yes”或甚至“YeS”。
请注意,费率是递进的,并且传输服务和分配费用,以及联邦、州和地方税,总共为每张账单增加了 26%。
解答
这里没有什么新的内容。处理递进费率是你已经学过的东西!如果这让你感到困惑,你需要刷新你的记忆并回顾相应的练习 22.4-5。
Python 程序如下。
file_37.2-3
def getConsumption():
consumption = int(input("输入消耗的千瓦时:"))
while consumption < 0:
print("错误:无效的数字!")
consumption = input("输入一个非负数:")
return consumption
def findAmount(kwh):
if kwh <= 400:
amount = kwh * 0.08
elif kwh <= 1500:
amount = 400 * 0.08 + (kwh - 400) * 0.22
elif kwh <= 2000:
amount = 400 * 0.08 + 1100 * 0.22 + (kwh - 1500) * 0.35
else:
amount = 400 * 0.08 + 1100 * 0.22 + 500 * 0.35 + (kwh - 2000) * 0.5
amount += 0.26 * amount
return amount
主代码从这里开始
while True:
kwh = getConsumption()
print("你需要支付:", findAmount(kwh))
answer = input("你想重复吗? ")
if answer.upper() != "YES": break
练习 37.2-4 掷骰子!
做以下操作:
i)编写一个名为 dice 的子程序,它返回 1 到 6 之间的随机整数。
ii)编写一个名为 searchAndCount 的子程序,它通过形式参数列表接受一个整数和一个列表,并返回该整数在列表中存在的次数。
iii)使用上述子程序编写一个 Python 程序,用 100 个随机整数(1 到 6 之间)填充一个列表,然后让用户输入一个整数。程序必须显示用户提供的整数在列表中出现的次数。
解答
这两个子程序都可以写成函数,因为它们都返回一个值。函数 dice()返回 1 到 6 之间的随机整数,函数 searchAndCount()返回一个表示整数在列表中存在的次数的数字。解决方案在此处展示。
file_37.2-4
from random import randrange
ELEMENTS = 100
def dice():
return randrange(1, 7)
def searchAndCount(x, a):
count = 0
for i in range(ELEMENTS):
if a[i] == x:
count += 1
return count
主代码从这里开始
a = [None] * ELEMENTS
for i in range(ELEMENTS):
a[i] = dice()
x = int(input())
print("提供的值在列表中存在", searchAndCount(x, a), "次")
练习 37.2-5:骰子中每个数字出现的次数是多少?
使用上一练习(练习 37.2-4)中引用的 dice()和 searchAndCount()函数,编写一个 Python 程序,用 100 个随机整数(介于 1 和 6 之间)填充一个列表,然后显示列表中每个数字出现的次数,以及哪个数字出现最频繁。
解决方案
如果你不使用循环控制结构来解决这个问题,你可以按照以下步骤进行。
将值 1 在列表 a 中出现的次数赋值给 n1
n1 = searchAndCount(1, a)
将值 2 在列表 a 中出现的次数赋值给 n2
n2 = searchAndCount(2, a)
.
.
.
将值 6 在列表 a 中出现的次数赋值给 n6
n6 = searchAndCount(6, a)
显示列表 a 中每个数字出现的次数
print(n1, n2, n3, n4, n5, n6)
找到 n1、n2、… n6 的最大值
maximum = n1
maxI = 1
if n2 > maximum:
maximum = n2
maxI = 2
.
.
.
if n6 > maximum:
maximum = n6
maxI = 6
显示列表中出现次数最多的数字。
print(maxI)
但现在你即将结束这本书的学习,当然,你可以做一些更有创意的事情。而不是将 searchAndCount()函数的每个结果分别赋值给 n1、n2、n3、n4、n5 和 n6 这些变量,你可以将这些结果赋值给名为 n 的列表的 0、1、2、3、4 和 5 位置,如下所示。
n = [None] * 6
for i in range(6):
n[i] = searchAndCount(i + 1, a)
在此之后,你可以使用你在第 33.3 节中学到的知识来找到列表 n 的最大值。
完整的解决方案如下所示。
file_37.2-5
from random import randrange
ELEMENTS = 100
def dice():
return randrange(1, 7)
def searchAndCount(x, a):
count = 0
for i in range(ELEMENTS):
if a[i] == x:
count += 1
return count
主代码从这里开始
创建列表 a,包含 1 到 6 之间的随机整数
a = [None] * ELEMENTS
for i in range(ELEMENTS):
a[i] = dice()
创建列表 n 并显示列表 a 中每个数字出现的次数
n = [None] * 6
for i in range(6):
n[i] = searchAndCount(i + 1, a)
print("Value", i + 1, "appears", n[i], "times")
找到列表 n 的最大值
maximum = n[0]
maxI = 0
for i in range(1, 6):
if n[i] > maximum:
maximum = n[i]
maxI = i
显示列表中出现次数最多的数字。
print("Value", maxI + 1, "appears in the list", maximum, "times.")
37.3 复习练习
完成以下练习。
1)执行以下操作:
i)编写一个名为 displayMenu 的子程序,显示以下菜单。
1)将美元(USD)转换为欧元(EUR)
2)将美元(USD)转换为英镑(GBP)
3)将美元(USD)转换为日元(JPY)
4)将美元(USD)转换为加拿大元(CAD)
5)退出
ii)编写四个不同的子程序,分别命名为 USD_to_EU、USD_to_GBP、USD_to_JPY 和 USD_to_CAD,它们通过它们的正式参数列表接受货币,然后返回相应的转换值。
iii)使用上述子程序编写一个 Python 程序,显示之前提到的菜单,然后提示用户输入一个选择(1、2、3、4 或 5)。如果选择了 1、2、3 或 4,程序必须提示用户输入金额,然后计算并显示相应的转换值。该过程必须根据用户的意愿重复进行。
已知:
►1 美元($1) = 0.94 欧元(EUR) (€)
►1 美元($1) = 0.81 英镑(GBP) (£)
►1 美元($1) = 149.11 日元(JPY)
►1 美元(\(1) = 1.36 加拿大元(CAD) (\))
2)执行以下操作:
i)编写一个名为 displayMenu 的子程序,显示以下菜单。
1)将美元(USD)转换为欧元(EUR)
2)将美元(USD)转换为英镑(GBP)
3)将欧元(EUR)转换为美元(USD)
4)将欧元(EUR)转换为英镑(GBP)
5)将英镑(GBP)转换为美元(USD)
6)将英镑(GBP)转换为欧元(EUR)
7)退出
ii)编写两个不同的子程序,分别命名为 USD_to_EUR 和 USD_to_GBP,它们通过其形式参数列表接受货币,然后返回相应的转换值。
iii)使用上述子程序编写一个 Python 程序,显示之前提到的菜单,然后提示用户输入一个选择(1 到 7)和一个金额。然后程序必须显示所需的价值。该过程必须根据用户的意愿重复进行。已知
►1 美元($1) = 0.94 欧元(EUR) (€)
►1 美元($1) = 0.81 英镑(GBP) (£)
3)执行以下操作:
i)编写一个名为 factorial 的子程序,该子程序通过其形式参数列表接受一个整数,并返回其阶乘。
ii)使用上述提到的 factorial()子程序编写一个名为 mySin 的子程序,该子程序通过其形式参数列表接受一个值,并返回 x 的正弦值,使用泰勒级数(如下所示)以 0.0000000001 的精度计算。
.
提示:请记住 x 是以弧度为单位的,并且
。
iii)编写一个名为 degreesToRad 的子程序,该子程序通过其形式参数列表接受一个角度(以度为单位),并返回其弧度等效值。已知 2π = 360°。
iv)使用上述提到的 mySin()和 degreesToRad()子程序编写一个 Python 程序,显示从 0°到 360°的所有整数的正弦值。
4)执行以下操作:
i)编写一个名为 isLeap 的子程序,该子程序通过其形式参数列表接受一个年份,并返回 True 或 False,取决于该年份是否为闰年。
ii)编写一个名为 numOfDays 的子程序,该子程序接受一个月份和年份,并返回该月份的天数。如果该月份是二月且年份是闰年,则子程序必须返回 29。
提示:使用上述提到的 isLeap()子程序。
iii)编写一个名为 checkDate 的子程序,该子程序接受一个日期(日、月、年)并返回 True 或 False,取决于该日期是否有效。
iv)使用上述子程序,编写一个 Python 程序,提示用户输入一个日期(日、月、年),然后计算并显示从用户提供的年份开始到用户提供的日期之间经过的天数。使用循环控制结构,程序必须验证数据输入,并在用户输入任何非有效日期时显示错误信息。
5)在电脑游戏中,玩家掷两个骰子。得分总和最高的玩家得一分。经过十次掷骰后,得分总和最高的玩家获胜。请执行以下操作:
i)编写一个名为 dice 的子程序,返回 1 到 6 之间的随机整数。
ii)使用上述子程序,编写一个 Python 程序,提示两位玩家输入他们的名字。然后,每位玩家连续“掷”两次骰子十次。获胜者是得分总和最高的玩家。
6)LAV 汽车租赁公司已租赁了 40 辆车,这些车分为三个类别:混合动力、汽油和柴油。公司根据以下表格对车辆进行收费。
| 天数 | 车型 |
|---|---|
| 汽油 | 柴油 |
| 1-5 天 | 每日$24 | 每日$28 |
| 6-8 天 | 每日$22 | 每日$25 |
| 9 天及以上 | 每日$18 | 每日$21 |
请执行以下操作:
i)编写一个名为 getChoice 的子程序,显示以下菜单。
1)汽油
2)柴油
3)混合动力
子程序随后提示用户输入汽车的类型(1、2 或 3),并将其返回给调用者。
ii)编写一个名为 getDays 的子程序,提示用户输入租赁的总天数,并将其返回给调用者。
iii)编写一个名为 getCharge 的子程序,接受通过形式参数列表传入的汽车类型(1、2 或 3)和总租赁天数,然后根据之前的表格返回应付款项。联邦、州和地方税费总计为每笔账单增加 10%。
iv)使用上述子程序,编写一个 Python 程序,提示用户输入关于租赁车辆的所有必要信息,然后显示以下信息:
a)每辆车的总费用(包括税费)
b)租赁的混合动力汽车总数
c)公司在扣除税费后的总净利润
请注意,这些费用是递增的。
7)TAM(电视观众测量)是媒体研究的一个专门分支,致力于量化电视观众信息。
LAV 电视观众测量公司统计了 10 个不同电视频道上主要新闻节目的观众人数。公司需要一个软件应用程序来获取一些有用的信息。请执行以下操作:
i)编写一个名为 getData 的子程序,提示用户输入每周每天(周一至周日)主要新闻节目的频道名称和观众人数,然后将这些列表返回给调用者。
ii) 编写一个名为 getAverage 的子程序,该子程序通过其形式参数列表接受一个一维列表,并返回前五个元素的平均值。
iii) 使用上述子程序编写一个 Python 程序,提示用户输入每个频道每天观众的名称和数量,然后显示以下内容:
a) 周末平均观众数量至少比一周中其他时间平均观众数量高出 20%的频道名称。
b) 每日观众数量持续增长的频道名称(如果有)。如果没有这样的频道,必须显示相应的消息。
- 一家民意调查公司询问 300 名公民在新冠疫情期间是否被隔离治疗。请执行以下操作:
i) 编写一个名为 inputData 的子程序,提示用户输入公民的社会安全号码(SSN)和他们的答案(是,否),分别输入到 SSNs 和 answers 两个列表中。这两个列表必须返回给调用者。
ii) 编写一个名为 sortLists 的子程序,该子程序通过其形式参数列表接受 SSNs 和 answers 列表。然后使用选择排序算法按升序对 SSNs 列表进行排序。子程序必须保持两个列表元素的一对一对应关系。
iii) 编写一个名为 searchList 的子程序,该子程序通过其形式参数列表接受 SSNs 列表和一个 SSN,然后返回该 SSN 在列表中的索引位置。如果 SSN 未找到,必须显示消息“SSN not found”,并返回值-1。使用二分查找算法。
iv) 编写一个名为 countAnswers 的子程序,该子程序通过其形式参数列表接受答案列表和一个答案。然后返回该答案在列表中存在的次数。
v) 使用上述子程序编写一个 Python 程序,提示用户输入公民的 SSN 和答案。然后提示用户输入一个 SSN,显示具有该 SSN 的公民给出的答案,以及与公民总数相比给出相同答案的百分比。程序必须询问用户是否要搜索另一个 SSN。如果答案是“是”,则过程必须重复;否则必须结束。
- 八支队伍参加足球锦标赛,每支队伍每周进行一场比赛,共进行 12 场比赛。请执行以下操作:
i) 编写一个名为 inputData 的子程序,提示用户输入每个队伍的名称以及每场比赛的“W”(胜利)、“L”(失败)或“T”(平局)字母,分别输入到 names 和 results 两个列表中。然后返回列表给调用者。
ii) 编写一个名为 displayResult 的子程序,它通过形式参数列表接受队伍名称和结果。然后,它提示用户输入一个字母(W、L 或 T),并为每个队伍显示它们分别赢得、输掉或平局的周数。例如,如果用户输入“L”,则子程序必须搜索并显示每个队伍输掉比赛的所有周数(例如,第 3 周、第 14 周,等等)。
iii) 编写一个名为 findTeam 的子程序,它通过形式参数列表接受队伍名称。然后,它提示用户输入一个队伍的名称,并返回该队伍在列表中的索引位置。如果用户提供的队伍名称不存在,则返回值 −1。
iv) 使用上述子程序,编写一个 Python 程序,提示用户输入每个队伍的名称以及每场比赛的“W”表示胜利、“L”表示失败或“T”表示平局(平局)。然后,它必须提示用户输入一个字母(W、L 或 T),并为每个队伍显示它们分别赢得、输掉或平局的周数。
最后,程序必须提示用户输入一个队伍的名称。如果找到用户提供的队伍,程序必须显示该队伍的总积分,然后提示用户输入另一个队伍的名称。只要用户输入现有的队伍名称,此过程必须重复进行。如果用户提供的队伍名称不存在,则显示消息“队伍未找到”,并且程序必须结束。
已知胜利获得 3 分,平局获得 1 分。
- 执行以下操作:
i) 编写一个名为 hasDuplicateDigits 的子程序,它接受一个整数,当其任何一位数字重复出现时返回 True;否则返回 False。
提示:声明一个包含 10 个元素的列表来跟踪每个数字的出现次数。该列表必须初始化为全部为零。
ii) 使用上述子程序,编写一个 Python 程序,提示用户输入一个整数,并显示一条消息,指出该整数的任何一位数字是否重复出现。此外,使用循环控制结构,程序必须验证数据输入,并在用户输入任何小于 11 的值时显示错误消息。
第四十七章:在“子程序”中复习
复习填字游戏
- 解决以下填字游戏。

Across
-
函数可能包含一个名为 _______ 的参数列表。
-
通常情况下,此子程序返回一个结果。
-
在这种编程中,问题被细分为更小的子问题。
-
一个数列,前两个数是 0 和 1,每个后续数是前两个数的和。
-
在这种编程中,具有共同功能的子程序被分组到单独的模块中。
-
向函数发送一个值。
-
Python 中的列表是通过 _________ 传递的。
Down
-
一种编程技术,其中子程序调用自身。
-
将一组语句打包成一个单元,执行特定任务。
-
通常情况下,此子程序不返回任何结果。
-
当子程序被调用时,传递的参数列表被称为 _______ 参数列表。
-
它指的是变量的作用范围。
-
原则上讲,这个子程序返回一个结果。
复习问题
回答以下问题。
-
什么是子程序?列举一些 Python 的内置子程序。
-
什么是过程式编程?
-
过程式编程的优点是什么?
-
什么是模块化编程?列举一个你了解的 Python 模块。
-
Python 函数的一般形式是什么?
-
如何调用函数?
-
描述主代码调用函数时执行的步骤。
-
什么是空函数?
-
Python 空函数的一般形式是什么?
-
如何调用空函数?
-
描述主代码调用空函数时执行的步骤。
-
函数和空函数之间有什么区别?
-
什么是形式参数列表?
-
实际参数列表是什么?
-
两个子程序可以使用相同名称的变量吗?
-
子程序的变量在主内存中“存活”多长时间?
-
主代码的变量在主内存中“存活”多长时间?
-
子程序可以调用另一个子程序吗?如果是,请给出一些例子。
-
“通过值传递参数”是什么意思?
-
“通过引用传递参数”是什么意思?
-
什么是可选参数?
-
“作用域”这个术语指的是什么?
-
当变量具有局部作用域时会发生什么?
-
当变量具有全局作用域时会发生什么?
-
局部变量和全局变量之间有什么区别?
-
递归是什么?
-
所有递归算法都必须遵循哪三条规则?
第八部分
面向对象编程
第三十八章
面向对象编程简介
38.1 什么是面向对象编程?
在第七部分中,你阅读或甚至编写的所有程序都在使用子程序(函数和空函数)。这种编程风格被称为过程式编程,大多数情况下都很不错!但是,当涉及到编写大型程序,或者在像微软、Facebook 或 Google 这样的大公司工作时,面向对象编程是必须使用的编程风格!
面向对象编程,通常简称为 OOP,是一种关注对象的编程风格。在 OOP 中,数据和功能被组合并封装在称为对象的东西中。应用面向对象编程原则可以使你更容易地维护代码,并编写其他人可以轻松理解和使用的代码。
“面向对象编程关注对象”的声明真正意味着什么?让我们考虑一个现实世界的例子。想象一辆车。你会如何描述一辆特定的车?它有特定的属性,如品牌、型号、颜色和车牌。此外,这辆车可以执行特定的动作,或者有人对它执行了这些动作。例如,有人可以打开或关闭它,加速或刹车,或者停车。
在 OOP 中,这辆车可以表示为一个具有特定属性(通常称为字段)的对象,它可以执行特定的动作(称为方法)。
显然,你现在可能正在问自己,“我如何首先创建对象?”答案是简单的!你需要的只是一个类。一个类就像一个“橡皮印泥章”!在图 38-1 中有一个章(这是类)和四个空字段以及三个动作(方法)。

图 38-1 一节课就像一个“橡皮印泥章”
使用这个章的人可以印出很多汽车(这些都是对象)。例如,在图 38-2 中,一个小男孩印出了那两辆汽车,然后他为它们上色,并为每辆汽车的各个字段填充了特定的属性。

图 38-2 你可以使用相同的橡皮章作为模板来印出许多汽车
创建一个新对象(类的新的实例)的过程被称为“实例化”。
类是一个模板,每个对象都是从一个类创建的。每个类都应该被设计来执行一个,并且只有一个任务!这就是为什么,大多数情况下,需要使用多个类来构建整个应用程序!
在面向对象编程(OOP)中,橡皮章就是类。你可以使用相同的类作为模板来创建(实例化)许多对象!
38.2 Python 中的类和对象
现在你已经掌握了类和对象背后的理论概念,让我们深入编写一个真正的 Python 类!下面的代码片段创建了 Car 类。类中有四个字段和三个方法。
class Car:
定义四个字段
brand = ""
model = ""
color = ""
licensePlate = ""
Define method turnOn()
def turnOn(self):
print("汽车开启")
Define method turnOff()
def turnOff(self):
print("汽车关闭")
Define method accelerate()
def accelerate(self):
print("汽车加速")
这里有一个有趣的细节:类内的字段和方法本质上只是普通的变量和子程序!
在面向对象编程(OOP)中,使用的术语是“方法”(而不是“函数”)和“void 方法”(而不是“void 函数”)。
Car 类只是一个模板。目前还没有创建任何对象!
无需现在就好奇这个 self 关键字是什么,它将在下一节(第 38.3 节)中详细解释。
类的名称应遵循大驼峰命名法,以及第 5.4 节中提到的所有变量命名规则。
要创建两个对象(换句话说,创建 Car 类的两个实例),你需要以下两行代码。
car1 = Car()
car2 = Car()
对象不过是一个类的实例,这就是为什么它很多时候可能被称为“类实例”或“类对象”。
当你创建一个新的对象(类的新的实例)时,这个过程被称为“实例化”。
现在你已经创建了(实例化)两个对象,你可以为它们的字段赋值。为此,使用点表示法。这意味着你需要写出对象的名称,然后是一个点,然后是你想要访问的字段或方法的名称。以下代码片段创建了两个对象,car1 和 car2,并将值赋给它们的字段。
car1 = Car()
car2 = Car()
car1.brand = "Mazda"
car1.model = "6"
car1.color = "Gray"
car1.licensePlate = "AB1234"
car2.brand = "Ford"
car2.model = "Focus"
car2.color = "Blue"
car2.licensePlate = "XY9876"
print(car1.brand) #它显示:马自达
print(car2.brand) #它显示:福特
在上面的例子中,car1 和 car2 是同一类的两个实例。使用 car1 和 car2 的点表示法可以让你一次只引用一个实例。如果你对其中一个实例进行任何更改,它将不会影响另一个实例!
The next code fragment calls the methods turnOff() and accelerate() of the objects car1 and car2 respectively.
car1.turnOff()
car2.accelerate()
类是一个不能执行的模板,而对象是类的实例,它可以被执行!
一个类可以被用来创建(实例化)你想要的任意数量的对象!
38.3 构造函数和关键字 self
在 Python 中,有一个具有特殊作用的方法,称为构造函数。构造函数在创建类的实例(对象)时自动执行。您想要在对象中进行的任何初始化都可以在这个方法中完成。在 Python 中,构造函数是一个名为 init() 的方法。
请注意,在名称的开头和结尾都有一个双下划线 init()。
看一下下面的例子。构造函数方法 init() 会自动调用两次,一次是在创建对象 p1 时,一次是在创建对象 p2 时,这意味着会显示两次消息“创建了一个对象”。
file_38.3a
class Person:
定义构造函数
def init(self):
print("创建了一个对象")
主代码从这里开始
p1 = Person() # 创建对象 p1
p2 = Person() # 创建对象 p2
正如您可能已经注意到的,在 init() 方法的形式参数列表中,有一个名为 self 的参数。在 Python 的面向对象编程(OOP)中,这个参数作为一个引用变量,指向当前对象。看一下下面的例子。
file_38.3b
class Person:
name = None
age = None
定义构造函数
def init(self):
print("创建了一个对象")
def sayInfo(self):
print("我是", self.name)
print("我今年", self.age, "岁")
主代码从这里开始
person1 = Person() # 创建对象 person1
为其字段分配值
person1.name = "John"
person1.age = 14
person1.sayInfo() # 调用对象 person1 的 sayInfo() 方法
尽管在调用方法的语句 person1.sayInfo() 中没有实际的参数,但在定义方法的语句 def sayInfo(self) 中确实存在一个形式参数(关键字 self)。显然,如果调用方式是 person1.sayInfo(person1),那就更正确了!这样写,会更有意义!实际的参数 person1 将被传递(分配)给形式参数 self!是的,这可能是更正确的做法,但请始终记住,Python 是一种“写得更少,做得更多”的语言!所以您不需要传递对象本身。Python 会为您做这件事!
如果您不记得形式参数或实际参数是什么,请重新阅读第 35.5 节。
请注意,在方法外部(但在类内部)声明字段时,您需要写出字段名称,而不使用点表示法。然而,从方法内部访问字段时,您必须使用点表示法(例如,self.name 和 self.age)。
你现在可能心中有一个问题:“为什么在方法 sayInfo()中需要将字段 name 和 age 引用为 self.name 和 self.age?在它们前面使用关键字 self 真的有必要吗?”一个简单的答案是,始终存在一种可能性,您在方法内部可能有两个额外的同名的局部变量(name 和 age)。因此,您需要一种方法来区分这些局部变量和对象的字段。如果您感到困惑,请尝试理解以下示例。在 MyClass 类中有一个字段 b,在 MyClass 类的 myMethod()方法中有一个局部变量 b。self 关键字用于区分局部变量和字段。
file_38.3c
class FooClass:
b = None #这是一个字段
def myMethod(self):
b = "***" #这是一个局部变量
print(b, self.b, b)
主代码从这里开始
x = FooClass() #创建对象 x
x.b = "Hello!" #为其字段赋值
x.myMethod() #它显示:*** Hello! ***
关键字 self 可以用于在类的方法中引用类的任何成员(字段或方法)。
38.4 向构造函数传递初始值
任何方法,即使是构造函数方法 init(),都可以在其形式参数列表中有形式参数。例如,在构造函数方法中,您可以使用参数在创建对象时传递一些初始值。以下示例创建了四个对象,每个对象代表希腊神话中的泰坦^([24])。
file_38.4a
class Titan:
name = None
gender = None
定义构造函数
def init(self, n, g):
self.name = n
self.gender = g
主代码从这里开始
titan1 = Titan("Cronus", "male") #创建对象 titan1
titan2 = Titan("Oceanus", "male") #创建对象 titan2
titan3 = Titan("Rhea", "female") #创建对象 titan3
titan4 = Titan("Phoebe", "female") #创建对象 titan4
请注意,尽管构造函数中有三个形式参数,但在调用构造函数的语句中只有两个实际参数。由于 Python 是一种“多做少写”的计算机语言,因此不需要传递对象本身。Python 会为您完成这项工作!
在 Python 中,一个字段和一个局部变量(甚至是一个形式参数)可以具有相同的名称是合法的。因此,Titan 类也可以写成如下形式
class Titan:
name = None
gender = None
定义构造函数
def init(self, name, gender ):
self.name = name #字段和参数可以具有相同的名称
self.gender = gender
变量 name 和 gender 是用于将值传递给构造函数的参数,而 self.name 和 self.gender 是用于在对象内部存储值的字段。
最后但同样重要的是,在 Python 中,您可以进一步简化 Titan 类。以下示例使用 Titan 类的简化版本。
file_38.4b
class Titan:
定义构造函数
def init(self, name, gender):
self.name = name
self.gender = gender
主代码从这里开始
titan1 = Titan("克洛诺斯", "male") #创建对象 titan1
titan2 = Titan("欧申纳斯", "male") #创建对象 titan2
titan3 = Titan("瑞亚", "female") #创建对象 titan3
titan4 = Titan("波雅", "female") #创建对象 titan4
print(titan1.name, "-", titan1.gender)
print(titan2.name, "-", titan2.gender)
print(titan3.name, "-", titan3.gender)
print(titan4.name, "-", titan4.gender)
38.5 类字段与实例字段
到目前为止,你所学的知识是,将字段声明在构造函数外部并不太糟糕,就像下面的程序所示。
class HistoryEvents:
day = None #此字段在内部声明
构造函数。它被称为“类字段”
定义构造函数
def init(self):
print("对象实例化")
主代码从这里开始
h1 = HistoryEvents() #创建对象 h1
h1.day = "4th of July"
h2 = HistoryEvents() #创建对象 h2
h2.day = "28th of October"
print(h1.day)
print(h2.day)
你还学会了可以重写此代码,并将字段 day 声明在构造函数内部,就像这里所示。
class HistoryEvents:
定义构造函数
def init(self, day):
print("对象实例化")
self.day = day #此字段在内部声明
构造函数。它被称为“实例字段”
主代码从这里开始
h1 = HistoryEvents("4th of July") #创建对象 h1
h2 = HistoryEvents("28th of October") #创建对象 h2
print(h1.day)
print(h2.day)
当字段在构造函数外部声明时,它被称为“类字段”,但当它在构造函数内部声明时,它被称为“实例字段”。
类字段由类的所有实例共享,而实例字段对每个实例都是唯一的。
所以,哪种编程风格更好?它们似乎都还不错!嗯,第二个不仅更好——你可以说这是编写类的正确方式!为什么?因为,在某些情况下,当可变数据结构(如列表和字典)用作类字段时,可能会产生不希望的结果。看看下面的例子。
class HistoryEvents:
events = [] #由所有实例共享的类字段
定义构造函数
def init(self, day):
self.day = day #每个实例唯一的实例字段
主代码从这里开始
h1 = HistoryEvents("4th of July") #创建对象 h1
为 h1 的字段赋值
h1.events.append("1776: 美国独立宣言")
h1.events.append("1810: 法国军队占领阿姆斯特丹")
h2 = HistoryEvents("28th of October") #创建对象 h2
为 h2 的字段赋值
h2.events.append("969: 拜占庭军队占领安条克")
h2.events.append("1940: 希腊的国庆日")
for event in h1.events:
print(event)
你可能预期最后一个 for 循环只会显示 7 月 4 日的两个事件。你的想法是正确的,但输出结果证明你是错的!最后一个 for 循环显示了四个事件,如图 38-3 所示。

图 38-3 当可变数据类型用作类字段时,可能会产生不希望的结果
列表事件是一个可变的数据结构。在 Python 中,可变数据结构不应用作类字段,因为这会产生不希望的结果。
建议尽可能少地使用类字段!使用实例字段!类字段越少,越好。
下一个示例是上一个正确版本的。
file_38.5
class HistoryEvents:
定义构造函数
def init(self, day):
self.day = day #每个实例独有的实例字段
self.events = [] #每个实例独有的实例字段
主代码从这里开始
h1 = HistoryEvents("7 月 4 日") #创建对象 h1
将值分配给 h1 的字段
h1.events.append("1776: 美利坚合众国独立宣言")
h1.events.append("1810: 法国军队占领阿姆斯特丹")
h2 = HistoryEvents("10 月 28 日") #创建对象 h2
将值分配给 h2 的字段
h2.events.append("969: 东罗马军队占领安条克")
h2.events.append("1940: 希腊的国庆日")
for event in h1.events:
print(event)
38.6 获取器和设置器方法与属性
字段是在类中直接声明的变量。然而,面向对象编程的原则表明,类中的数据应该是隐藏的,并且免受意外更改。想象一下,有一天你可能会编写其他程序员将在他们的程序中使用的类。所以,你不想让他们知道你的类里面是什么!你的类的内部操作应该对外界保密。通过不暴露字段,你成功地隐藏了类的内部实现。字段应保持为类的私有,并通过获取和设置方法(或通过属性)访问。
一般来说,程序员应该只使用具有私有或受保护访问权限的数据字段。在 Java 或 C# 中,您可以使用特殊关键字将字段设置为私有或受保护。
让我们通过一个示例来尝试理解所有这些新内容。假设你编写了以下将华氏温度转换为摄氏温度等价的类的代码。
file_38.6a
class FahrenheitToCelsius:
def init(self, value):
self.temperature = value
此方法获取温度
def getTemperature(self):
return 5.0 / 9.0 * (self.temperature - 32.0)
主代码从这里开始
x = FahrenheitToCelsius(-68) #创建对象 x
print(x.getTemperature())
这个类几乎完美,但有一个主要缺点。它没有考虑到温度不能低于-459.67 华氏度(-273.15 摄氏度)。这个温度被称为绝对零度。因此,一个对物理一无所知的初学者程序员可能会像下面的代码片段所示,将-500 华氏度的值传递给构造函数。
x = FahrenheitToCelsius(-500) #创建对象 x
打印(x.getTemperature())
尽管程序可以完美运行并显示-295.55 摄氏度的值,但遗憾的是,这个温度在整个宇宙中根本不存在!所以这个类的略微不同的版本可能部分解决了这个问题。
file_38.6b
class FahrenheitToCelsius:
def init(self, value):
self.setTemperature(value) #使用方法设置字段 temperature 的值
此方法获取温度
def getTemperature(self):
return 5.0 / 9.0 * (self.temperature - 32.0)
此方法设置温度
def setTemperature(self, value):
if value >= -459.67:
self.temperature = value
else:
raise ValueError("没有低于-459.67 的温度")
主代码从这里开始
x = FahrenheitToCelsius(-50) #创建对象 x。这会调用构造函数,它,
依次调用 setter。
打印(x.getTemperature())
raise 语句强制程序抛出异常(运行时错误),导致执行流程停止。
这次,使用了一个名为 setTemperature()的方法来设置字段 temperature 的值。这比之前好,但并不完全完美,因为程序员必须小心,并且每次想要更改字段 temperature 的值时都必须记住使用这个方法。问题是字段 temperature 的值仍然可以直接通过其名称直接更改,如下面的代码片段所示。
x = FahrenheitToCelsius(-50) #创建对象 x
打印(x.getTemperature())
x.setTemperature(-65) #这是可以的!
打印(x.getTemperature())
x.temperature = -500 #遗憾的是,这仍然被允许!
打印(x.getTemperature())
这正是应该使用属性的地方!属性是一个类成员,它提供了一个灵活的机制来读取、写入或计算你想要保持私有的字段的值。属性暴露字段,但隐藏实现!
file_38.6c
class FahrenheitToCelsius:
def init(self, value):
self.temperature = value #属性被初始化。这会调用 setter
def getTemperature(self):
return 5.0 / 9 * (self._temperature - 32)
def setTemperature(self, value):
if value >= -459.67:
self._temperature = value
else:
raise ValueError("没有低于-459.67 的温度")
定义一个属性
temperature = property(getTemperature, setTemperature)
主代码从这里开始
x = FahrenheitToCelsius(-50) #创建对象 x。这会调用构造函数,它,
依次调用 setter。
print(x.temperature) #This calls the getter
x.temperature = -65 #This calls the setter.
print(x.temperature) #This calls the getter.
x.temperature = -500 #This calls the setter and throws an error
print(x.temperature)
Note the underscore ( _ ) at the beginning of the field temperature. In Python, an underscore at the beginning of a variable name can be used to denote a “private field”.
So, what does the statement temperature = property(getTemperature, setTemperature) do anyway?
When a statement tries to access the value of the field temperature, the getTemperature() method is called automatically and similarly, when a statement tries to assign a value to the field temperature the setTemperature() method is called automatically! So, everything seems to be okay now! But is it, really?
One last thing can be done to make things even better! You can completely get rid of the methods getTemperature() and setTemperature() because you don't want to have two ways to access the value of the field temperature, as shown in the code fragment that follows
x = FahrenheitToCelsius(0) #Create object x
There are still two ways to access the value of the field _temperature
x.setTemperature(-100) #Use the method
x.temperature = -100 #Use the property
In order to completely get rid of the methods getTemperature() and setTemperature() you can use some of the decorators that Python supports.
file_38.6d
class FahrenheitToCelsius:
def init(self, value):
self.temperature = value #Property is initialized. This calls the setter
Use the decorator @property to define the getter
@property
def temperature(self):
return 5.0 / 9 * (self._temperature - 32)
Use the decorator @field_name.setter to define the setter
@temperature.setter
def temperature(self, value):
if value >= -459.67:
self._temperature = value
else:
raise ValueError("There is no temperature below -459.67")
Main code starts here
x = FahrenheitToCelsius(-50) #Create object x. This calls the constructor which,
in turn, calls the setter.
print(x.temperature) #This calls the getter.
x.temperature = -65 #This calls the setter.
print(x.temperature) #This calls the getter.
x.temperature = -500 #This calls the setter and throws an error
print(x.temperature)
Please note that the two methods and the field share the same name, temperature.
A decorator is a function that takes another function as an argument and returns a new, prettier version of that function. Decorators allow you to change the behavior or extend the functionality of a function without changing the function's body.
练习 38.6-1 罗马数字
罗马数字如下表所示。
| 数字 | 罗马数字 |
|---|---|
| 1 | I |
| 2 | II |
| 3 | III |
| 4 | IV |
| 5 | V |
Do the following:
i)Write a class named Romans which includes
a)定义一个名为 number 的属性。它将用于获取和设置名为 _number 的私有字段的整数值。设置器在数字未识别时必须抛出错误。
b)定义一个名为 roman 的属性。它将用于获取和设置名为 _number 的私有字段的罗马数字格式的值。设置器在罗马数字未识别时必须抛出错误。
ii)使用上述类,编写一个 Python 程序,显示与值 3 对应的罗马数字以及与罗马数字值“V”对应的数字。
解决方案
属性 number 的获取器和设置器非常简单,因此没有特别之处要解释。然而,属性 roman 的获取器和设置器需要一些解释。
属性 roman 的获取器可以编写如下
定义获取器
@property
def roman(self):
if self._number == 1
return "I"
elif self._number == 2
return "II"
elif self._number == 3
return "III"
elif self._number == 4
return "IV"
elif self._number == 5
return "V"
然而,由于你现在对字典了解很多,你可以使用更好的方法,如下面的代码片段所示。
定义获取器
@property
def roman(self):
number2roman = {1: "I", 2: "II", 3: "III", 4: "IV", 5: "V"}
return number2roman[self._number]
因此,设置器可以如下所示
定义设置器
@roman.setter
def roman(self, key):
roman2number = {"I": 1, "II": 2, "III": 3, "IV": 4, "V": 5}
if key in roman2number:
self._number = roman2number[key]
else:
raise ValueError("罗马数字未识别")
最终的 Python 程序如下
file_38.6-1
class Romans:
定义获取器
@property
def number(self):
return self._number
定义设置器
@number.setter
def number(self, value):
if value >= 1 and value <= 5:
self._number = value
else:
raise ValueError("数字未识别")
定义获取器
@property
def roman(self):
number2roman = {1: "I", 2: "II", 3: "III", 4: "IV", 5: "V"}
return number2roman[self._number]
定义设置器
@roman.setter
def roman(self, key):
roman2number = {"I": 1, "II": 2, "III": 3, "IV": 4, "V": 5}
if key in roman2number:
self._number = roman2number[key]
else:
raise ValueError("罗马数字未识别")
主代码从这里开始
x = Romans() #创建对象 x
x.number = 3
print(x.number) #显示:3
print(x.roman) #显示:III
x.roman = "V"
print(x.number) #显示:5
print(x.roman) #显示:V
38.7 一个方法能否调用同一类中的另一个方法?
在第 36.2 节中,你学习了子程序可以调用另一个子程序。显然,当涉及到方法时,情况也是一样的——一个方法可以调用同一类中的另一个方法!毕竟,方法不过是子程序而已!所以,如果你想让一个方法调用同一类中的另一个方法,你应该在你想调用的方法前使用关键字 self(使用点符号)如以下示例所示。
file_38.7
class JustAClass:
def foo1(self):
print("foo1 was called")
self.foo2() #Using dot notation to call foo2()
def foo2(self):
print("foo2 was called")
Main code starts here
x = JustAClass()
x.foo1() #Call foo1() which, in turn, will call foo2()
练习 38.7-1 做数学题
Do the following:
i)Write a class named DoingMath which includes
a)a void method named square that accepts a number through its formal argument list and then calculates its square and displays the message “The square of XX is YY”, where XX and YY must be replaced by actual values.
b)a void method named squareRoot that accepts a number through its formal argument list and then calculates its square root and displays the message “The square root of XX is YY” where XX and YY must be replaced by actual values. However, if the number is less than zero, the method must display an error message.
c)a void method named displayResults that accepts a number through its formal argument list and then calls the methods square() and squareRoot() to display the results.
ii)使用上述类,编写一个 Python 程序,提示用户输入一个数字。然后程序必须显示该数字的根和平方根。
Solution
这个练习相当简单。方法 square()、squareRoot()和 displayResults()必须在它们的正式参数列表中有一个形式参数,以便接受传递的值。解决方案如下。
file_38.7-1
from math import sqrt
class DoingMath:
def square(self, x): #Argument x accepts passed value
print("The square of", x, "is", x ** 2)
def squareRoot(self, x): #Argument x accepts passed value
if x < 0:
print("Cannot calculate square root")
else:
print("Square root of", x, "is", sqrt(x))
def displayResults(self, x): #Argument x accepts passed value
self.square(x)
self.squareRoot(x)
Main code starts here
dm = DoingMath()
b = float(input("Enter a number: "))
dm.displayResults(b)
38.8 类继承
类继承是面向对象编程的主要概念之一。它允许你使用另一个类作为基础来编写一个类。当一个类基于另一个类时,程序员会说“它继承了另一个类”。被继承的类称为父类、基类或超类。执行继承的类称为子类、派生类或子类。
子类会自动继承父类的所有方法和字段。然而,最好的部分是,你可以在子类中添加额外的特征(方法或字段)。因此,当你需要编写多个具有许多共同特征但又不完全相同类的代码时,你会使用继承。为此,你需要这样做。首先,编写一个包含所有共同特征的父类。接下来,编写继承自父类所有这些共同特征的子类。最后,添加任何额外的和独特的特征,这些特征是针对每个子类的。就像人类一样,正是这些额外的和独特的特征将子类与其父类区分开来,对吧?
假设你想编写一个程序来跟踪学校中的教师和学生。他们有一些共同的特征,例如姓名和年龄,但他们也有一些特定的特征,如教师的薪水和学生最后的成绩,这些特征不是共同的。你可以在这里做的是编写一个名为 SchoolMember 的父类,它包含教师和学生共有的所有特征。然后你可以编写两个子类,分别命名为 Teacher 和 Student,一个用于教师,一个用于学生。这两个子类都可以继承自 SchoolMember 类,但需要在子类 Teacher 和 Student 中相应地添加额外的字段,名为 salary 和 finalGrade。
父类 SchoolMember 如此展示
class SchoolMember:
def init(self, name, age):
self.name = name
self.age = age
print("A school member was initialized")
如果你想让一个类继承自类 SchoolMember,它必须按照以下方式定义
class Name(SchoolMember):
def init(self, name, age [, …]):
Call the constructor of the class SchoolMember
super().init(name, age)
为此类定义额外的字段
Additional statement or block of statements
为此类定义额外的方法和/或属性
其中 Name 是子类的名称。
因此,类 Teacher 可以如下定义
class Teacher(SchoolMember):
def init(self, name, age, salary):
Call the constructor of the class SchoolMember
super().init(name, age)
self.salary = salary #This is an additional field for this class
print("A teacher was initialized") #This is an additional statement for this constructor
This is an additional method for this class
def displayValues(self):
print("Name:", self.name)
print("Age:", self.age)
print("Salary:", self.salary)
The statement super().init(name, age) 调用了 SchoolMember 类的构造函数,并初始化了 Teacher 类的字段 name 和 age。
同样,类 Student 可以如下定义
class Student(SchoolMember):
def init(self, name, age, finalGrade):
Call the constructor of the class SchoolMember
super().init(name, age)
self.finalGrade = finalGrade #This is an additional field for this class
print("一个学生被初始化") #这是构造函数的附加语句
这是类的附加方法
def displayValues(self):
print("姓名:", self.name)
print("年龄:", self.age)
print("最终成绩:", self.finalGrade)
语句 super().init(name, age)调用了 SchoolMember 类的构造函数,并初始化了 Student 类的 name 和 age 字段。
完整的 Python 程序如下。
file_38.8
定义 SchoolMember 类。
class SchoolMember:
def init(self, name, age):
self.name = name
self.age = age
print("一个学校成员被初始化")
定义类 Teacher。它继承自类 SchoolMember。
class Teacher(SchoolMember):
def init(self, name, age, salary):
调用 SchoolMember 类的构造函数
super().init(name, age)
self.salary = salary #这是类的附加字段
print("一个教师被初始化") #这是构造函数的附加语句
这是类的附加方法
def displayValues(self):
print("姓名:", self.name)
print("年龄:", self.age)
print("薪水:", self.salary)
定义类 Student。它继承自类 SchoolMember。
class Student(SchoolMember):
def init(self, name, age, finalGrade):
调用 SchoolMember 类的构造函数
super().init(name, age)
self.finalGrade = finalGrade #这是类的附加字段
print("一个学生被初始化") #这是构造函数的附加语句
这是类的附加方法
def displayValues(self):
print("姓名:", self.name)
print("年龄:", self.age)
print("最终成绩:", self.finalGrade)
主代码从这里开始
teacher1 = Teacher("Mr. John Scott", 43, 35000)
teacher2 = Teacher("Mrs. Ann Carter", 5, 32000)
student1 = Student("Peter Nelson", 14, "A")
student2 = Student("Helen Morgan", 13, "B")
teacher1.displayValues()
teacher2.displayValues()
student1.displayValues()
student2.displayValues()
38.9 复习问题:对/错
对以下每个陈述选择对或错。
1)在编写大型程序时,过程式编程比面向对象编程更好。
2)面向对象编程侧重于对象。
3)一个对象结合了数据和功能。
4)面向对象编程使你更容易维护代码,但你的代码不容易被他人使用。
5)你可以不使用类来创建一个对象。
6)创建类的新实例的过程被称为“安装”。
7)在 OOP 中,你总是必须创建至少两个同一类的实例。
8)当创建一个对象时,会执行 init()方法。
9)当你创建同一类的两个实例时,类的 init()方法将执行两次。
10)当字段在构造函数外部声明时,它被称为“实例字段”。
11)类字段被类的所有实例共享。
12)面向对象编程的原则规定,类的数据应该被隐藏并且防止意外修改。
13)属性是一个类成员,它提供了一个灵活的机制来读取、写入或计算字段的值。
14)属性暴露了类的内部实现。
15)类继承是面向对象编程的主要概念之一。
16)当一个类被继承时,它被称为“派生类”。
17)父类会自动继承子类的所有方法和字段。
38.10 复习练习
完成以下练习。
1)执行以下操作
i)编写一个名为 Geometry 的类,它包括
a)一个名为 rectangleArea 的方法,它通过其形式参数列表接受矩形的底边和高度,然后计算并返回其面积。
b)一个名为 triangleArea 的方法,它通过其形式参数列表接受三角形的底边和高度,然后计算并返回其面积。已知
.
ii)使用上述类,编写一个 Python 程序,提示用户输入正方形的边长、矩形的底边和高度以及三角形的底边和高度,然后显示每个形状的面积。
2)执行以下操作
i)编写一个名为 Pet 的类,它包括
a)一个构造函数
b)一个名为 kind 的实例字段
c)一个名为 legsNumber 的实例字段
d)一个名为 startRunning 的无返回值方法,它显示消息“Pet is running”。
e)一个名为 stopRunning 的无返回值方法,它显示消息“Pet stopped”。
ii)编写一个 Python 程序,创建两个名为 Pet 的类的实例(例如,一只狗和一只猴子),然后调用它们的一些方法。
3)执行以下操作
i)在上一个练习的 Pet 类中
a)将字段 kind 和 legsNumber 更改为私有字段 _kind 和 _legsNumber。
b)添加一个名为 kind 的属性。它将用于获取和设置字段 _kind 的值。当字段设置为空值时,setter 必须抛出错误。
c)添加一个名为 legsNumber 的属性。它将用于获取和设置字段 _legsNumber 的值。当字段设置为负值时,setter 必须抛出错误。
d)将构造函数更改为通过其形式参数列表接受属性 kind 和 legsNumber 的初始值。
ii)编写一个 Python 程序,创建一个名为 Pets(例如,一只狗)的类的实例,然后调用它的两个方法。然后尝试为属性 kind 和 legsNumber 设置错误值,看看会发生什么。
4)执行以下操作
i)编写一个名为 Box 的类,它包括
a)一个构造函数,它通过其形式参数列表接受三个名为 _width, _length 和 _height 的私有字段的初始值。
b)一个名为 displayVolume 的无返回值方法,它计算并显示一个长方体的体积,其尺寸为 _width, _length 和 _height。已知
体积 = 宽度 × 长度 × 高度
c)一个名为 displayDimensions 的无返回值方法,它显示长方体的尺寸。
ii)使用上述类,编写一个 Python 程序,提示用户输入 30 个盒子的尺寸,然后显示它们的尺寸和体积。
提示:创建一个包含 30 个 Box 类对象的列表。
5)在上一练习的 Box 类中添加三个属性:width、length 和 height。它们将用于获取和设置字段 _width、_length 和 _height 的值。当相应的字段设置为负值或零时,setter 必须抛出错误。
6)执行以下操作
i)编写一个名为 Cube 的类,包括
a)a 构造函数,通过形式参数列表接受一个名为 _edge 的私有字段的初始值。
b)一个无返回值的方法名为 displayVolume,用于计算并显示边长为 _edge 的立方体的体积。已知
体积 = 边长³
c)一个名为 displayOneSurface 的无返回值方法,用于计算并显示边长为 _edge 的立方体一个面的表面积。
d)一个名为 displayTotalSurface 的无返回值方法,用于计算并显示边长为 _edge 的立方体的总表面积。已知
总表面积 = 6 × 边长²
ii)使用上述类,编写一个 Python 程序,提示用户输入立方体的边长,然后显示其体积、其中一个面的表面积和总表面积。
7)在上一练习的 Cube 类中添加一个名为 edge 的属性。它将用于获取和设置私有字段 _edge 的值。当字段设置为负值或零时,setter 必须抛出错误。
8)执行以下操作
i)编写一个名为 Circle 的类,包括
a)a 构造函数和一个名为 _radius 的私有字段,初始值为-1。
b)一个名为 radius 的属性。它将用于获取和设置字段 _radius 的值。当字段尚未设置时,getter 必须抛出错误,而当字段设置为负值或零时,setter 必须抛出错误。
c)一个名为 getDiameter 的方法,用于计算并返回半径为 _radius 的圆的直径。已知
直径 = 2 × 半径
d)一个名为 getArea 的方法,用于计算并返回半径为 _radius 的圆的面积。已知
面积 = 3.14 × 半径²
e)一个名为 getPerimeter 的方法,用于计算并返回半径为 _radius 的圆的周长。已知
周长 = 2 × 3.14 × 半径
ii)编写一个名为 displayMenu 的子程序,显示以下菜单。
1)输入半径
2)显示半径
3)显示直径
4)显示面积
5)显示周长
6)退出
iii)使用上述子程序和类,编写一个 Python 程序,显示之前提到的菜单并提示用户输入一个选择(1 到 6)。如果选择 1,程序必须提示用户输入一个半径。如果选择 2,程序必须显示在选择 1 中输入的半径。如果选择 3、4 或 5,程序必须相应地显示半径等于选择 1 中输入的半径的圆的直径、面积或周长。该过程必须重复用户希望进行的次数。
- 假设你在一个即将创建文字处理应用程序的计算机软件公司工作。你被分配编写一个类,该类将用于向用户提供信息。
i)编写一个名为 Info 的类,包括
a)一个名为 userText 的属性。它将用于获取和设置名为 _userText 的私有字段的值。当字段设置为空值时,setter 必须抛出一个错误。
b)一个名为 getSpacesCount 的方法,它返回 userText 属性包含的总空格数。
c)一个名为 getWordsCount 的方法,它返回 userText 属性包含的总单词数。
d)一个名为 getVowelsCount 的方法,它返回 userText 属性包含的元音字母总数。
e)一个名为 getLettersCount 的方法,它返回 userText 属性包含的总字符数(不包括空格)。
ii)使用上述类,编写一个测试程序,提示用户输入文本,然后显示所有可用信息。假设用户只输入空格字符或字母(大写或小写),单词由单个空格字符分隔。
提示:在一个三个单词的文本中,有两个空格,这意味着单词总数比空格总数多一个。计算空格总数,然后你可以轻松地找到单词总数!
- 在二战后的冷战期间,消息被加密,以便如果敌人截获它们,没有解密密钥就无法解密。一个非常简单的加密算法是字母旋转。该算法将所有字母向上移动 N 步,其中 N 是加密密钥。例如,如果加密密钥是 2,你可以通过将字母 A 替换为字母 C,将字母 B 替换为字母 D,将字母 C 替换为字母 E,以此类推来加密一条消息。 执行以下操作:
i)编写一个名为 EncryptDecrypt 的类,包括
a)一个构造函数和一个名为 _encrDecrKey 的私有字段,其初始值为-1。
b)一个名为 encrDecrKey 的属性。它将用于获取和设置名为 _encrDecrKey 的字段的值。当字段尚未设置时,getter 必须抛出一个错误,当字段未设置为 1 到 26 之间的值时,setter 必须抛出一个错误。
c)一个名为 encrypt 的方法,它通过其形式参数列表接受一条消息,然后返回加密后的消息。
d)一个名为 decrypt 的方法,它通过形式参数列表接受一个加密消息,然后返回解密后的消息。
ii)编写一个名为 displayMenu 的子程序,显示以下菜单:
1)输入加密/解密密钥
2)加密消息
3)解密消息
4)退出
iii)使用上述子程序和类,编写一个 Python 程序,显示之前提到的菜单,然后提示用户输入一个选择(1 到 4)。如果选择 1,程序必须提示用户输入加密/解密密钥。如果选择 2,程序必须提示用户输入一个消息,然后显示加密后的消息。如果选择 3,程序必须提示用户输入一个加密消息,然后显示解密后的消息。此过程必须根据用户的意愿重复进行。假设用户只输入小写字母或空格作为消息。
11)执行以下操作:
i)编写一个名为 Vehicle 的父类,它包含
a)一个构造函数,它通过形式参数列表接受三个实例字段名为 numberOfWheels、color、length、width 和 height 的初始值。
b)两个名为 startEngine 和 stopEngine 的 void 方法,分别显示消息“引擎已启动”和“引擎已停止”。
ii)编写一个名为 Car 的子类,它继承自 Vehicle 类。此外,它还包含
a)一个带有额外实例字段 bootCapacity 和初始值零的构造函数。
b)一个名为 turnWindshieldWipersOn 的 void 方法,显示消息“雨刷已被打开!”。
iii)编写一个名为 Motorcycle 的子类,它继承自 Vehicle 类。此外,它还必须包含
a)一个带有额外实例字段 hasLuggage 和初始值 False 的构造函数。
b)一个名为 doAWheelie 的 void 方法,显示消息“我在做轮滑!!!”
iv)使用上述类,编写一个 Python 程序,创建两个 Car 类的实例和一个 Motorcycle 类的实例,为它们的字段分配一些值,然后调用它们的所有方法。
12)按照以下方式修改第 38.8 节 - 类继承(文件 _38.8)的 Python 程序:
i)在 SchoolMember 类中,将字段 name 和 age 更改为私有字段 _name 和 _age,并为它们添加 getter 和 setter 方法。当 _name 字段的 setter 方法设置为空值时必须抛出错误,而 _age 字段的 setter 方法在设置为负值或零时必须抛出错误。
ii)在 Teacher 类中,将字段 salary 更改为私有字段 _salary,并为它添加 getter 和 setter 方法。当字段设置为负值时,setter 方法必须抛出错误。
iii)在 Student 类中,将字段 finalGrade 改为私有字段 _finalGrade,并为它添加 getter 和 setter 方法。setter 方法必须在字段被设置为 A、B、C、D、E 或 F 之外的值时抛出错误。
13)修改前一个练习中的 Python 程序,使其使用属性而不是 getter 和 setter 方法。
第四十九章:面向对象编程回顾
回顾填字游戏
1)解决以下填字游戏。

横向
1)一个类实例。
4)这允许你在不改变函数体的情况下改变函数的行为或扩展其功能。
8)创建新对象的过程。
9)一个对象的属性。
10)init()方法的名字。
向下
2)在构造函数外部声明的字段被称为 ______ 字段。
3)类 __________ 允许你使用另一个类作为基类来编写类。
5)对象-___________ 编程是一种关注对象的编程风格。
6)在构造函数内部声明的字段被称为 ______ 字段。
7)对象执行的动作。
回答问题
回答以下问题。
1)什么是面向对象编程?
2)类的构造函数是什么?
3)Python 中的装饰器做什么?
4)在什么情况下你必须使用点符号来写字段名?
5)self 关键字是什么?
6)解释类字段和实例字段之间的区别。
7)为什么在面向对象编程(OOP)中不应该暴露字段?
8)获取器和设置器方法是什么?
9)Python 中的属性做什么?
10)“类继承”这个术语是什么意思?
第五十章:第 IX 部分
文件
第三十九章
文件介绍
39.1 简介
你迄今为止看到的所有程序都可以描述为“临时”的。尽管它们读取一些输入数据并显示一些输出结果,但所有这些值都存储在计算机主内存(RAM)中的变量、列表和其他数据结构中;因此,当程序执行完毕时,这些值都会丢失。即使这种情况没有发生,当你关闭计算机时,它们肯定也会丢失。然而,有许多情况下,你需要将这些值保存在更持久的存储设备中,例如硬盘驱动器(HDD)或固态硬盘(SSD)。
Python 可以读取存储在计算机文件中的输入数据或将输出结果写入同一文件或不同文件。这种读取/写入过程称为文件 I/O(文件输入/输出),可以使用 Python 的一些现成函数和方法实现。
通常,所使用的文件类型是文本文件。文本文件包含一系列字符,并存储在永久存储设备(硬盘驱动器(HDD)、固态硬盘(SSD)等)中。
在计算机编程中使用的另一种类型的文件是“二进制文件”;然而,这种类型超出了本书的范围,将不再进一步分析。
在以下章节中,你将学习如何打开和关闭文本文件,如何从文本文件中读取值或写入值,甚至如何在其中搜索值。
39.2 打开文件
要使用文件(用于读取、写入或追加),你需要做的第一件事是打开文件。Python 语句的一般形式是
descriptor = open(filename [, mode])
where
►描述符是文件对象的名称,可以用来从文件中读取、写入或追加内容。
►文件名是一个字符串,包含存储在硬盘(或任何其他存储设备,如 SSD、闪存 U 盘等)中的文件夹(目录)和文件名。
►模式是一个字符串,它定义了你想访问文件文件名的方式。此参数是可选的。如果省略,其默认值是“r”。此参数的三个基本值如下表所示。
| 模式 | 描述 |
|---|---|
| r | 它以读取模式打开文本文件。文件指针位于文件开头(位置 0)。如果文件文件名不存在,Python 将抛出运行时错误。 |
| w | 它以写入模式打开文本文件。如果文件文件名已存在,Python 将覆盖它;否则,Python 将创建一个新文件。 |
| a | 它以追加模式打开文本文件,保留文件中的任何先前内容。文件指针位于文件末尾。如果文件文件名不存在,Python 将创建一个新文件。 |
在某种程度上,文件指针可以比作列表的索引。你将在第 39.5 节中了解更多关于文件指针的内容。
让我们看看一些示例。
示例 1
以下语句
f = open("names.txt", "r") # 等价于:f = open("names.txt")
打开文本文件“names.txt”进行读取。文件“names.txt”必须位于源代码保存的同一文件夹(目录)中。如果文件不存在,Python 会抛出运行时错误。
open()函数是 Python 的内置函数。您不需要从任何库中导入它。
示例 2
以下语句
fgrades = open("c:/temp/grades.txt", "w")
在文件夹(目录)“c:/temp”中创建文本文件“grades.txt”并打开它进行写入。如果文件已存在,Python 将覆盖它。
注意,文件路径的定义使用斜杠(/)而不是反斜杠(\)字符。
示例 3
以下语句
fgrades = open("c:/temp/students/grades.txt", "a")
打开文本文件“grades.txt”进行追加。该文件必须位于文件夹(目录)“c:/temp”的子文件夹(子目录)“students”中。如果文件不存在,Python 将创建一个新文件。
39.3 关闭文件
在完成对文件的读取、写入或追加操作后,使用 close()方法关闭文件至关重要。此方法表示文件的使用已完成,这将导致操作系统(OS)保存主内存(RAM)中可能存在的任何未保存数据。close()方法的通用形式如下:
descriptor.close()
其中,描述符是用于打开文件的文件对象的名称。
让我们看看一些示例。
示例 1
以下代码片段打开文本文件“c:/temp/data.txt”进行读取,并在最后关闭它。
fst = open("c:/temp/data.txt")
一个语句或语句块
fst.close()
注意,当省略模式参数时,默认值是 "r"。
示例 2
以下代码片段打开文本文件“temperatures.txt”进行追加,并在最后关闭它。
f = open("temperatures.txt", "a")
一个语句或语句块
f.close()
39.4 在文件中写入(或追加)字符串
要在文件中写入字符串(甚至追加字符串),可以使用 write()方法。此方法的通用形式如下:
descriptor.write(str)
其中
►描述符是用于打开文件的文件对象的名称。
►str 是要写入(或追加到)文件中的字符串值。
以下示例在文件夹(目录)“c:/temp”中创建文件“f_data39.4-i.txt”。如果文件“f_data39.4-i.txt”已存在,Python 将覆盖它;否则,Python 将创建一个新文件。然后,程序使用 write()方法在文件中写入三个字符串。
file_39.4a
PATH = "c:/temp/"
fout = open(PATH + "f_data39.4-i.txt", "w")
fout.write("Good Morning")
fout.write("Good Evening")
fout.write("Good Night")
fout.close()
尝试执行上述程序,然后找到并打开(使用记事本应用程序)最近创建的“c:/temp/f_data39.4-i.txt”文件。你在文件中看到的是以下内容:
Good MorningGood EveningGood Night
所有三行字符串都写在了同一行。这是因为,与您熟悉的 print 语句不同,write() 方法不会在字符串末尾自动添加“换行符”。
要打开一个文本文件并查看其中的内容,您可以使用简单的记事本应用程序,例如 Windows 的记事本。或者,您可以从以下地址免费下载并使用 Notepad++ 应用程序:
要强制 Python 写入“换行符”,可以使用特殊的字符序列 \n(在第 6.2 节中介绍)。下一个示例将打开之前创建的文件“c:/temp/f_data39.4-i.txt”进行追加。随后,将写入一个“换行符”和三行文本。
file_39.4b
PATH = "c:/temp/"
fout = open(PATH + "f_data39.4-i.txt", "a")
fout.write("\n")
fout.write("Hello!\n")
fout.write("Hi!\n")
fout.write("Bye!\n")
fout.close()
如果您执行此程序,然后使用记事本应用程序定位并打开“c:/temp/f_data39-i.txt”文件,您现在将看到以下内容:
Good MorningGood EveningGood Night
Hello!
Hi!
Bye!
在打开文件进行追加之前,第一行“Good MorningGood EveningGood Night”已经存在于文件中。
下一个示例在“c:/temp”文件夹中创建文件“f_data39.4-ii.txt”。如果文件“f_data39.4-ii.txt”已存在,Python 将覆盖它,否则,Python 将创建一个新文件。然后,程序使用 write() 方法在文件中写入 10 个字符串,每个字符串占一行。
file_39.4c
PATH = "c:/temp/"
fout = open(PATH + "f_data39.4-ii.txt", "w")
for i in range(1, 11):
fout.write("Line " + str(i) + "\n")
fout.close()
write() 方法的参数必须是字符串类型。如果您想在文件中写入一个数字(整数或浮点数),您必须首先使用 str() 函数将其转换为字符串。
39.5 文件指针
如前所述,文件指针与列表索引非常相似。两者都用于指定读取信息或写入新信息的起点。然而,文件指针与列表索引的主要区别在于,每次执行读取或写入操作时,前者会自动移动。
假设一个文件已经包含消息“HELLO JOHN\nHI ALL!”。如果您为此特定文件打开读取,文件指针将自动放置在文件的开头,如下所示。

如果你现在执行读取操作(如下一节所述),读取将从文件指针指示的位置开始,指针将自动向前移动,移动的位数与读取的字符数相同。以下是从文件中读取 6 个字符时文件指针将处于的位置。

随后的读取操作将从单词“JOHN”开始的位置开始。
相反,如果你为追加而打开一个文件,文件指针将自动定位在文件末尾,如下所示:

如果你随后执行写操作,写入将从文件指针指示的位置开始,指针将自动向前移动,移动的位数与你在文件中写入的字符数相同。

39.6 从文件中读取
假设文件“f_data39.6.txt”包含以下文本。
15.3 15 向下取整
22.6 23 向上取整
55.5 56 再次向上取整
读取到当前行的末尾
要从文件当前位置读取所有字符直到当前行的末尾并将它们赋值给变量 var_name_str,你可以使用以下一般形式的语句
var_name_str = descriptor.readline()
其中
►descriptor 是用于打开文件的文件对象名称。
►var_name_str 可以是任何类型的字符串变量。
下面的程序将文件“f_data39.6.txt”的三个行赋值给变量 s1, s2 和 s3。
file_39.6a
PATH = "c:/temp/"
fin = open(PATH + "f_data39.6.txt", "r")
s1 = fin.readline()
s2 = fin.readline()
s3 = fin.readline()
fin.close()
print(s1, end = "") #它显示:15.3 15 向下取整
print(s2, end = "") #它显示:22.6 23 向上取整
print(s3, end = "") #它显示:55.5 56 再次向上取整
正如你所知,参数 end = "" 取消了打印语句末尾的“换行符”。然而,如果你执行前面的示例,你会注意到文件的三行是依次显示的。这是因为文件中(每行的末尾)已经存在换行符(特殊字符序列 \n),并且它们被分配给了变量 s1, s2 和 s3。如果你想去除这些换行符,可以使用 strip() 方法(在第 14.3 节中介绍)。
读取一些字符或直到文件末尾
要从当前位置读取指定数量的字符并将它们赋值给变量 var_name_str,你可以使用以下一般形式的语句。请注意,length 参数是可选的。如果省略,则从当前位置读取直到文件末尾的所有字符。
var_name_str = descriptor.read([length])
其中
►描述符是用于打开文件的文件对象的名称。
►var_name_str 可以是任何字符串类型的变量。
►长度可以是任何大于 0 的整数类型的值。
以下程序将文件“f_data39.6.txt”的前两个值分配给变量 d1、i1,并将第一行的文本分配给变量 s1,同时移除换行符。然后,对文件的第二行执行相同的操作,将值分配给变量 d2、i2 和 s2。
file_39.6b
PATH = "c:/temp/"
fin = open(PATH + "f_data39.6.txt", "r")
d1 = fin.read(4) #将'15.3'作为字符串读取
i1 = fin.read(3) #将' 15'作为字符串读取(包括前导空格)
s1 = fin.readline().strip() #读取第一行文本并移除换行符
d2 = float(fin.read(4)) #将 22.6 作为浮点数读取
i2 = int(fin.read(3)) #将 23 作为整数读取
s2 = fin.readline().strip() #读取第二行文本并移除换行符
fin.close()
print(d1) #它显示:15.3
print(i1) #它显示: 15
print(s1) #它显示:向下取整
print(d2) #d2 是一个浮点数。它显示:22.6
print(i2) #i2 是一个整数。它显示:23
print(s2) #它显示:向上取整
readline()方法从文件当前位置读取所有字符,直到当前行的末尾。
在下一个示例中,read()方法将文件“f_data39.6.txt”的全部内容分配给变量 s。
file_39.6c
PATH = "c:/temp/"
fin = open(PATH + "f_data39.6.txt", "r")
s = fin.read()
fin.close()
print(s)
39.7 遍历文件内容
遍历文件内容有三种方法。
假设文件“f_to_be_or_not_to_be.txt”包含以下文本:
要是,还是不是:这是个问题:
是否在心灵中忍受
生活的箭矢和命运的狂傲,
或者拿起武器对抗海上的麻烦,
通过对抗结束它们?去死:去睡;
现在,让我们看看所有三种方法。
第一种方法
这种方法将文件的全部内容分配给一个变量,然后使用 for 结构遍历变量的字符,如下面的示例所示。
file_39.7a
PATH = "c:/temp/"
fin = open(PATH + "f_to_be_or_not_to_be.txt", "r")
s = fin.read() #将文件内容分配给变量 s
fin.close() #关闭文件。不再需要保持打开状态!
遍历变量 s 的内容
for i in range(len(s)):
print(s[i])
下一个示例几乎做了同样的事情,但没有使用变量 s。
file_39.7b
PATH = "c:/temp/"
fin = open(PATH + "f_to_be_or_not_to_be.txt", "r")
for letter in fin.read():
print(letter)
fin.close()
这种方法在处理大文件时并不适用。
第二种方法
一种更好的方法是使用 for 结构直接访问文件的内容的每一行来迭代文件。以下示例显示文件的所有行,每次迭代一行。
file_39.7c
PATH = "c:/temp/"
fin = open(PATH + "f_to_be_or_not_to_be.txt", "r")
for line in fin:
print(line, end = "")
fin.close()
第三种方法
另一种方法是使用 while 循环直接访问文件的内容的每一行来迭代文件。以下示例显示文件的所有行,每次迭代一行。
file_39.7d
PATH = "c:/temp/"
fin = open(PATH + "f_to_be_or_not_to_be.txt", "r")
line = fin.readline()
while line != "":
print(line, end = "")
line = fin.readline()
fin.close()
read()和 readline()方法在当前位置处于文件末尾时返回空字符串。
39.8 复习问题:正确/错误
对于以下每个陈述,选择正确或错误。
1)当你关闭计算机时,文件的内容会丢失。
2)如果你使用“r”参数打开文件,这意味着如果文件不存在,Python 会创建一个新的文件。
3)如果你使用“a”参数打开文件,这意味着如果文件不存在,Python 会创建一个新的文件。
4)如果你使用“w”参数打开文件,这意味着如果文件不存在,Python 会抛出一个运行时错误。
5)f = open("filename", "a")语句会覆盖文件 filename(当文件已存在时)。
6)以下程序(不是代码片段)是正确的
ff = open("grades.txt", "r")
print(ff.readline())
fff.close()
7)以下代码片段是正确的
f = open("grades.txt", "w")
print(f.read())
8)以下代码片段是正确的
f = open("grades.txt")
f.write("OK")
9)以下代码片段是正确的
f = open("grades.txt", "w")
f.write("OK")
10)如果文件“test.txt”中有 10 个字符,执行以下程序后,文件的大小会变大。
f = open("test.txt", "w")
f.write("你好")
f.close()
11)执行以下程序后,文件“test.txt”的大小会变大。
f = open("test.txt", "a")
f.write("你好")
f.close()
12)以下代码片段是正确的。
f = open("c:/data/test.txt", "w")
f.write(10)
f.close()
13)重复执行以下程序三次后,文件“test.txt”中只有两行文本。
f = open("test.txt", "a")
f.write("早上好\n")
f.write("晚上好\n")
f.close()
14)重复执行以下程序三次后,文件“test.txt”中只有两行文本。
f = open("test.txt", "w")
f.write("早上好")
f.write("晚上好")
f.close()
15)重复执行以下程序三次后,文件“test.txt”中只有两行文本。
f = open("test.txt", "w")
f.write("早上好\n")
f.write("晚上好\n")
f.close()
-
readline() 方法从文件中读取一行。
-
read() 方法总是读取文件中的所有字符。
-
你不能使用 while 循环遍历文件的内容。
-
你不能使用 for 循环遍历文件的内容。
-
假设文件“test.txt”中有两行文本。在执行以下代码片段后,用户屏幕上只显示一行文本。
fin = open("data.txt")
for line in fin:
print(line.strip(), end = "")
fin.close()
-
如果当前位置在文件的末尾,并且执行了 read() 方法,Python 返回一个空字符串。
-
如果文件“test.txt”包含以下文本
Hello
World!
然后,以下代码片段在屏幕上显示“LOL!”,不带双引号。
x = []
f = open("test.txt")
x.append(f.readline())
x.append(f.readline())
f.close()
a = ""
a += x[0][2]
a += x[0][4]
a += x[1][3]
a += x[1][5]
print(a.upper())
39.9 复习练习
完成以下练习。
-
编写一个 Python 程序,创建一个文本文件并将一周中的日子(星期日、星期一等),每个一行写入。
-
编写一个 Python 程序,从上一个练习中创建的文件中读取一周中的日子(星期日、星期一等)并将它们存储到一个列表中。然后,程序必须以与它们在列表中存储的顺序相反的顺序显示一周中的日子。
-
编写一个 Python 程序,将“*** 文件结束 ***”文本追加到上一个练习中的文件中,不带双引号。
-
编写一个 Python 程序,将 50 个随机整数(介于 1 和 100 之间)写入名为“randoms.txt”的文件中,每个一行。
-
编写一个 Python 程序,创建 10 个文件,分别命名为“file1.txt”、“file2.txt”等,“file10.txt”,并在每个文件中写入一个随机的 3 位整数。
-
编写一个 Python 程序,将以下乘法表写入文件。
1 x 1 = 1
1 x 2 = 2
1 x 3 = 3
1 x 4 = 4
2 x 1 = 2
2 x 2 = 4
2 x 3 = 6
2 x 4 = 8
…
…
10 x 1 = 10
10 x 2 = 20
10 x 3 = 30
10 x 4 = 40
-
编写一个 Python 程序,显示文件中每行的字符数。
-
编写一个 Python 程序,显示文件中存在的字符数和行数。
-
编写一个 Python 程序,对于文件的每一行,如果该行中有标点符号(仅检查逗号、句号和感叹号),则显示消息“第 XX 行有标点符号”。请注意,XX 必须替换为实际值。
第四十章
更多关于文件的内容
40.1 文件的一般练习
练习 40.1-1 计算 10 个数字的总和
假设有一个名为“f_data40.1-1.txt”的文件,其中包含 10 个三位整数(由单个空格字符分隔)。文件结构的示例如下。
131 500 122 152 127 191 111 290 156 161
编写一个 Python 程序,计算并显示它们的总和。
解答
有两种方法可以遍历此文件中的数字。
第一种方法
在这种方法中,一个名为 values 的字符串变量获取文件整行的内容。然后,在 for 循环中使用切片机制将内容分割成单独的三位数。然后使用 int()函数将这些数字转换为整数,以计算它们的总和。
file_40.1-1a
PATH = "c:/temp/"
fin = open(PATH + "f_data40.1-1.txt")
values = fin.readline()
fin.close()
total = 0
for i in range(10):
total += int(values[i * 4 : i * 4 + 3])
print(total)
第二种方法
使用 for 循环读取和处理文件中的每个 10 个整数。在循环内部,fin.read(3)方法从文件中读取下一个 3 个字符。由于文件包含三位整数,这读取了一个整数。int()函数将读取的字符串转换为整数,然后将其添加到总和中。接下来,for 循环中的第二个语句读取并忽略分隔整数的空格字符。
file_40.1-1b
PATH = "c:/temp/"
fin = open(PATH + "f_data40.1-1.txt")
total = 0
for i in range(10):
total += int(fin.read(3)) #读取 3 个字符
space = fin.read(1) #读取并忽略空格字符
fin.close()
print(total)
练习 40.1-2 计算未知数量数字的平均值
假设有一个名为“f_data40.1-2.txt”的文件,其中包含数字,每行一个,除了最后一行包含短语“文件结束”。文件结构的示例如下。
16
13.172
33.5
.
.
文件结束
编写一个 Python 程序,计算并显示它们的平均值。
解答
根据第 28.3 节中讨论的“终极”规则,while 循环应该如下,以一般形式给出。
sNumber = fin.readline()
while sNumber != "文件结束":
一个语句或语句块
sNumber = fin.readline()
最终程序如下。
file_40.1-2
PATH = "c:/temp/"
fin = open(PATH + "f_data40.1-2.txt")
total = 0
count = 0
sNumber = fin.readline()
while sNumber != "文件结束":
total += float(sNumber)
count += 1
sNumber = fin.readline()
fin.close()
if count > 0:
print(total / count)
练习 40.1-3 查找最小和最大值
假设有一个名为“f_data40.1-3.txt”的文件,其中包含数字,每行一个。文件结构的示例如下。
16
13.172
33.5
.
.
Write a Python program that finds the greatest and lowest values and stores them in a file named “output.txt” in the following form
33.5 13.172
Assume that there is at least one value in the file “f_data40.1-3.txt”.
Solution
The final program is as follows.
file_40.1-3
PATH = "c:/temp/"
fin = open(PATH + "f_data40.1-3.txt")
Read the first value
maximum = float(fin.readline())
minimum = maximum
Read the rest of the values, line by line
for sNumber in fin:
number = float(sNumber)
if number > maximum:
maximum = number
if number < minimum:
minimum = number
fin.close()
Write the greatest and lowest value in a file
fout = open(PATH + "output.txt", "w")
fout.write(str(maximum) + " " + str(minimum))
fout.close()
Exercise 40.1-4 Concatenating Files
Suppose there are two text files named “text1.txt” and “text2.txt”. Write a Python program that concatenates the contents of the two files and writes the concatenated text in a third file named “final.txt”, placing the contents of the file “text1.txt” before the contents of the file “text2.txt”.
Solution
This exercise can be solved using several approaches. Let's see two of them.
First approach
The program opens the file “text1.txt”, reads all of its contents, assigns them to the variable contents, and then closes the file. Next, it opens the file “text2.txt”, reads all of its contents, concatenates them with the previous ones (those in the variable contents), and closes the file. Finally, it opens the file “final.txt” and writes the concatenated contents in it, as shown in the program that follows.
file_40.1-4a
PATH = "c:/temp/"
fin = open(PATH + "text1.txt")
contents = fin.read()
fin.close()
fin = open(PATH + "text2.txt")
contents += fin.read() #Concatenation
fin.close()
fout = open(PATH + "final.txt", "w")
fout.write(contents)
fout.close()
Second approach
The program that follows opens all the three files at the beginning, writes the concatenated contents, and then closes them.
file_40.1-4b
PATH = "c:/temp/"
fin1 = open(PATH + "text1.txt")
fin2 = open(PATH + "text2.txt")
fout = open(PATH + "final.txt", "w")
fout.write(fin1.read() + fin2.read())
fout.close()
fin2.close()
fin1.close()
The order in which you close the files doesn't need to correspond to the order in which you opened them. You can open, for example, the file “text2.txt” first, and close it last, or open it first and close it first.
Exercise 40.1-5 Searching in a File
In the United States, the Social Security Number (SSN) is a nine-digit identity number applied to all U.S. citizens in order to identify them for the purposes of Social Security. Suppose there is a file named “SSN.txt” that contains the SSNs (Social Security Numbers) of all citizens of the state of California, as well as their full name, one record on each line. An example of the structure of the file is shown here.
123456789 Aristides Bouras
123000000 Loukia Ainarozidou
121212121 John Papas Junior
.
.
编写一个 Python 程序,提示用户输入要搜索的 SSN 的前几位数字,然后在该文件中进行搜索并显示所有以这些数字开头的公民的姓名和姓氏。
解决方案
在这个特定的练习中,如果用户输入了完整的九位 SSN 进行搜索,并且找到了这个 SSN,程序必须显示相应公民的姓名和姓氏,并停止搜索。另一方面,如果用户输入少于九位数字进行搜索,程序必须搜索并显示所有以这些数字开头的公民的姓名和姓氏。这个练习的解决方案如下。
file_40.1-5
PATH = "c:/temp/"
ssnToSearch = input("输入要搜索的 SSN:")
length = len(ssnToSearch)
fin = open(PATH + "SSN.txt")
found = False
for line in fin:
ssn = line[:9]
fullName = line[10:]
如果找到了
if ssnToSearch == ssn[0:length]:
print(fullName, end = "") #变量 fullName 已经包含一个换行符
found = True
如果要搜索的 SSN 包含 9 位数字并且找到了,退出循环
if length == 9:
break
fin.close()
if not found:
print("未找到!")
练习 40.1-6 使用子程序组合文件
做以下操作:
i)编写一个名为 readContent 的子程序,它接受一个文件的名称并返回其内容。
ii)编写一个名为 writeContent 的子程序,它接受一个文件的名称和一个字符串,并将该字符串写入文件。
iii)使用上述子程序,编写一个 Python 程序,提示用户输入两个文件的名称,然后将第一个文件的内容复制到第二个文件中。假设用户输入了有效的文件名。
解决方案
尽管这个特定的练习看起来相当简单,但有必要强调一些关于 readContent()函数的事情。检查下面的 readContent()函数,并尝试找出它包含的错误。这个错误是一个逻辑错误,而不是语法错误。这个函数在没有 Python 解释器抛出任何语法错误的情况下成功执行。然而,错误确实存在,并且很难发现!
def readContent(filename):
fin = open(filename)
return fin.read()
fin.close()
这里发生的情况是,当执行返回语句时,Python 会忽略函数中的其余语句,这意味着文件可能不会关闭。想象一下在程序中多次调用这个特定的函数。你最终会得到许多永远不会关闭的打开文件。
Python 的最新版本可能会在没有引用(fin)的情况下自动关闭文件,但留下文件打开并等待 Python 为您关闭它是一种不良做法。
一个好的做法是将返回语句放在函数的末尾,如下面的程序所示。
file_40.1-6
def readContent(filename):
fin = open(filename)
contents = fin.read()
fin.close()
return contents
def writeContent(filename, contents):
fout = open(filename, "w")
fout.write(contents)
fout.close()
主代码从这里开始
source = input("Enter source filename: ")
destination = input("Enter destination filename: ")
c = readContent(source) #相当于:
writeContent(destination, c) #writeContent(destination, readContent(source))
40.2 复习练习
完成以下练习。
1)假设有一个名为“f_data40.2-1.txt”的文件,其中包含 10 个两位整数(由单个空格字符分隔)。以下是该文件结构的示例。
13 55 12 61 12 19 80 91 15 16
编写一个 Python 程序,计算并显示大于 50 的平均值。
2)假设有一个名为“f_data40.2-2.txt”的文件,其中包含三位整数(由逗号字符分隔)。以下是该文件结构的示例。
130,501,322,415,527,191
编写一个 Python 程序,计算并显示那些在 300 到 500 之间的平均值。假设文件中至少有一个数字。
3)假设有一个名为“f_data40.2-3.txt”的文件,其中包含班级学生的成绩和全名(由逗号字符分隔),每行一个记录。以下是该文件结构的示例。
96,George Papas
100,Anna Maria Garcia
89,Peter Smith
.
.
编写一个 Python 程序,找到并显示班级中最好和最差学生的全名。假设文件中至少有一个记录,并且所有成绩都是不同的。
4)运输公司的 IT 管理员需要一个程序从名为“f_data40.2-4.txt”的文件中提取有关公司运输物品的有用信息。假设该文件包含每个物品的宽度、长度、高度和描述。物品的尺寸以英寸为单位,每个尺寸在文件中占用 4 个字符(整数部分 2 个字符,小数点 1 个字符,小数位 1 个字符)。以下是该文件结构的示例:
110.5 011.2 020.9 盒子编号 37(塑料瓶)
022.6 023.1 040.2 容器编号 23(6 个玻璃瓶)
009.5 156.6 020.0 包裹编号 12(易碎物品)
024.2 020.1 030.1 容器编号 29(玻璃瓶)
编写一个 Python 程序:
i)提示用户输入一个关键字以在物品描述中进行搜索。例如,如果用户输入单词“glass”,则程序必须显示以下消息
关键词'glass'找到!
容器编号 23(6 个玻璃瓶)- 尺寸:22.6 x 23.1 x 40.2
容器编号 29(玻璃瓶)- 尺寸:24.2 x 20.1 x 30.1
ii)找到并显示每个物品的体积(以立方英尺为单位)。消息必须格式化如下:
每个物品的体积:
盒子编号 37(塑料瓶):体积 = 14.9686 立方英尺
容器编号 23(6 个玻璃瓶):体积 = 12.1451 立方英尺
包裹编号 12(易碎物品):体积 = 17.2187 立方英尺
容器编号 29(玻璃瓶):体积 = 8.472940 立方英尺
iii)找到并显示所有项目的总体积(以立方英尺为单位)。
iv)找到并显示体积最大的盒子的描述。
请记住,一立方英尺等于 1728 立方英寸。
5)编写一个 Python 程序,提示用户输入两个文件的文件名。然后程序必须将两个文件的 内容连接起来,并将连接后的文本写入名为“final.txt”的第三个文件中,将第一个文件的内容放在第二个文件的内容之后。如果用户提供的文件名不包含“ .txt”扩展名,程序必须显示错误消息。
6)假设有一个名为“f_data40.2-6.txt”的文件,其中包含 15 个数字,每个数字占一行。编写一个 Python 程序,使用冒泡排序算法将这些数字按升序排序,并将排序后的值写入同一文件中,位于初始未排序值下方。
7)假设有一个名为“f_data40.2-7.txt”的文件,其中包含八个城市的名称以及它们在特定一天的最高温度。文件的结构示例如下。
纽约
82.3
华盛顿特区
84.3
.
.
因此,奇数行包含城市名称,偶数行包含每个城市的最高温度。编写一个 Python 程序,逐行读取文件,并将城市名称和温度分别存储在“cities”和“temperatures”列表中。假设文件中至少有一个城市名称及其对应温度。然后程序必须
i)计算并显示所有城市的平均气温。
ii)找到并显示最高温度以及所有具有该温度的城市名称。
8)有些单词如“革命性的”和“国际化”非常长,反复书写它们会相当累人。如果单词的长度超过 10 个字符,则认为该单词过长。在这种情况下,必须用特殊缩写替换该单词:你保留单词的首尾字母,并在它们之间插入字母的数量。例如,“革命性的”变为“r11y”,“国际化”变为“i18n”。
假设有一个名为“f_data40.2-8.txt”的文件,其中包含英文文本。执行以下操作:
i)编写一个名为“abbreviate”的子程序,它接受一个单词,如果它超过 10 个字符长,则返回其缩写;否则返回相同的单词。
ii)使用上述子程序,编写一个 Python 程序,从文件中读取文本,并显示所有长单词的缩写形式。
假设单词之间由单个空格字符分隔。
9)Pig Latin 是一种在讲英语的国家中常用的有趣的语言游戏。它涉及根据一组简单的规则改变单词的字母。以下是翻译单词到 Pig Latin 的规则:
►如果单词以元音开头,只需将“way”添加到单词的末尾。例如,“apple”变为“appleway”。
►如果单词以一个或多个辅音开头,将辅音(s)移动到单词的末尾并添加“ay”。例如,“banana”变为“ananabay”和“flower”变为“owerflay”。
假设有一个名为“f_data40.2-9.txt”的文件,其中包含英文文本。执行以下操作:
i)编写一个名为 pigLatinTranslator 的子程序,该程序接受一个英语单词并返回相应的 Pig Latin 翻译。
ii)使用上述子程序,编写一个 Python 程序,该程序从文件中读取文本,并将相应的 Pig Latin 翻译写入名为“pig_latin_translation.txt”的文件。
假设文本仅包含英文字母的小写字母,单词之间由单个空格字符分隔。
10)给定两个字符串,X = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 和 Y = "JKWCTAMEDXSLFBYUNG RZOIQVHP",你可以加密任何消息。持有 Y 字符串的人可以通过将字符串 X 中的字母逐个映射到字符串 Y 来解密加密消息。更具体地说,字母“A”被加密为“J”,字母“B”为“K”,字母“C”为“W”,依此类推。根据这种加密方法,编写一个 Python 程序,提示用户输入一条消息,然后将加密消息写入名为“encrypted.txt”的文件。空格字符不得加密,应保持原样。
11)编写一个 Python 程序,该程序解密上一个练习(在文件“encrypted.txt”中)的消息,并将解密后的消息写入名为“decrypted.txt”的文件。
12)编写一个名为 copyFile 的子程序,该程序接受两个参数(源和目标),然后使用名称 destination 创建源文件的新副本。
13)假设有一个名为“f_data40.2-13.txt”的文件,其中包含三角形所有三边的长度(每行一个)。文件结构的示例如下。
16.0
20.6
22.7
执行以下操作
i)编写一个名为 Triangle 的类,包括
a)一个构造函数,从文件中读取三个边并将其分配给三个名为 _sideA、_sideB 和 _sideC 的私有字段。
b)编写一个名为 canBeTriangle 的方法,该方法检查并返回 True,当字段 _sideA、_sideB 和 _sideC 的值可以作为三角形的三边长度时。否则必须返回 False。
提示:在任何三角形中,每边的长度都小于其他两边长度的总和。
c)编写一个名为 displayLengths 的 void 方法,该方法显示三边的长度以及一个消息,指出这些长度是否可以是三角形的三边长度。
d)编写一个名为 displayArea 的 void 方法,如果长度可以是三角形的三边长度,则计算并显示三角形的面积。你可以使用近 2000 年来已知的赫伦公式!
.
其中 S 是半周长 
e)一个名为 displayPerimeter 的 void 方法,如果长度可以是三角形的三边长度,则计算并显示三角形的周长。
ii)使用上述类,编写一个 Python 程序,显示关于三角形的所有可用信息。
第五十三章:在“文件”部分复习
填字游戏复习
1)解决以下填字游戏。

横向
1) 完成从文件读取后,你总是需要 ______ 文件。
5) 这个指针与列表的索引非常相似。
6) 有两种类型的文件,文本文件和 ______ 文件。
纵向
1) 这就是文本文件包含的内容。
2) 这项操作必须在任何读取/写入操作之前执行。
3) 当你以追加模式打开文件时,文件指针位于文件的 ______ 处。
4) 文件存储在这种存储设备中。
复习问题
回答以下问题。
1)什么是文本文件?
2)文本文件有什么用途?
3)close()方法究竟做了什么?
4)read()方法和 readline()方法有什么区别?
5)你学过多少种遍历文件内容的方法?每种方法给出一个示例。
第五十四章:作者的一些最后的话
我希望您阅读这本书时感到非常愉快。我尽了我所能确保这本书对所有人都有益且易于理解,即使是对编程没有先验经验的人也是如此。
如果您觉得这本书很有价值,请考虑访问您购买它的网络商店,以及goodreads.com,通过撰写正面评论并给予您认为合适的星级来表示您的赞赏。这样做将激励我继续写作,当然,您也将帮助其他读者发现我的作品。
并且始终记住:学习是一个终身的、持续的过程,它从出生开始,贯穿你的一生!
脚注
[1] “算法”这个词来源于“algorism”和希腊语单词“arithmos”。单词“algorism”来自阿尔-花剌子密的拉丁化名字,而希腊语单词“arithmos”意为“数字”。
[2] 穆罕默德·本·阿尔-花剌子密(780-850)是一位波斯数学家、天文学家和地理学家。他被认为代数学的奠基人之一。
[3] 唐纳德·厄文·克努特(1938- ),是一位杰出的美国计算机科学家和数学家,被誉为“算法分析的之父”。他撰写了有影响力的多卷本作品《计算机程序设计艺术》,并在计算复杂性分析和文献编程方面做出了开创性的贡献。
[4] 科拉多·博姆(1923-2017)是一位计算机科学家,特别以其对结构化编程理论的贡献以及函数式编程语言的实现而闻名。
[5] 朱塞佩·雅各比尼(1936-2001)是一位计算机科学家。他最有影响力的贡献是关于结构化编程的定理,该定理与科拉多·博姆于 1966 年共同发表,标题为《流程图、图灵机和只有两个形成规则的编程语言》。
[6] 格蕾丝·穆雷·霍珀(1906-1992)是一位美国计算机科学家和海军上将。她是哈佛马克 I 计算机的第一批程序员之一,并开发了用于 A-0 计算机编程语言的第一个编译器,后来又开发了第二个,称为 B-0 或 FLOW-MATIC。
[7] 乔治·布尔(1815-1864)是一位英国数学家、哲学家和逻辑学家。他最著名的是构建了现在称为布尔逻辑(布尔代数)的架构,这是现代数字计算机的基础。
[8] 丹尼尔·加布里埃尔·华氏(1686-1736)是一位德国物理学家、工程师和玻璃吹制工,他最著名的发明是酒精温度计和水银温度计,以及现在以他的名字命名的温度尺度。
[9] 威廉·汤姆森,第一代开尔文男爵(1824-1907),是一位爱尔兰出生的英国数学物理学家和工程师。他因发展绝对零度(开尔文温度尺度的基础)而广为人知,因此温度单位以他的名字命名。他在热电学中发现了汤姆森效应,并帮助发展了热力学第二定律。
[10] 安德斯·塞 elsius(1701-1744)是一位瑞典天文学家、物理学家和数学家。他在瑞典创立了乌普萨拉天文台,并提出了以他的名字命名的摄氏温度尺度。
[11] 亚历山大港的赫伦(约公元 10 年-约公元 70 年)是一位古代希腊数学家、物理学家、天文学家和工程师。他被认为是古代最伟大的实验家。他描述了第一个记录的蒸汽涡轮发动机,称为“风车”(有时称为“赫伦发动机”)。赫伦还描述了一种迭代计算正数平方根的方法。然而,今天他最出名的是证明“赫伦公式”,该公式可以从三角形的边长求出面积。
[12] 萨摩斯的毕达哥拉斯(约公元前 571 年-约公元前 497 年)是一位著名的希腊数学家、哲学家和天文学家。他因证明重要的毕达哥拉斯定理而闻名。他是柏拉图的影响者。他的理论至今仍被应用于数学中。
[13] 威廉·莎士比亚(1564-1616)是一位英国诗人、剧作家和演员。他常被称为英格兰的国民诗人。他写了大约 40 部戏剧和几首长篇叙事诗。他的作品被认为是世界文学的最好代表之一。他的戏剧已被翻译成所有主要的活语言,并且至今仍在上演。
[14] 一个要么为零要么为正的量。
[15] 弗朗西斯·博福特(1774-1857)是一位爱尔兰海图绘制员和英国皇家海军军官。他是博福特风力等级的发明者。
[16] 一个要么为零要么为负的量。
[17] 华氏温度计上-459.67°(的值)是可能达到的最低温度,被称为绝对零度。绝对零度对应摄氏温度尺度上的-273.15°C 和开尔文温度尺度上的 0K。
[18] 一个要么为零要么为负的量。
[19] 桑伽马格拉马的马哈瓦(约 1340-约 1425),是来自印度桑伽马格拉马镇(今伊里安贾卢卡杜)的一位印度数学家和天文学家。他创立了喀拉拉天文数学学校,并首次使用无穷级数近似值来计算各种三角函数。他常被称作“数学分析之父”。
[20] 戈特弗里德·威廉·莱布尼茨(1646-1716)是一位德国数学家和哲学家。他在形而上学、逻辑、哲学以及数学、物理和历史等领域做出了重要贡献。在他的著作《组合艺术》(1666 年出版)中,他提出了一种被认为是现代计算机理论祖先的模型。
[21] 列奥纳多·皮萨诺·比格洛(约 1170-约 1250),也被称为斐波那契,是一位意大利数学家。在他的著作《算术大全》(1202 年出版)中,斐波那契使用一个特殊的数字序列来尝试确定兔子种群的增长。今天,这个数字序列被称为斐波那契数列。他还是第一个将阿拉伯数字系统引入欧洲的人;这是我们今天使用的基于十个数字、小数点和零的数字系统。在此之前,罗马数字系统正在被使用,这使得数值计算变得困难。
[22] 布鲁克·泰勒(1685-1731)是一位英国数学家,他最著名的贡献是泰勒级数和对有限差分理论的贡献。
[23] 萨缪尔·芬利·布里斯·莫尔斯(1791-1872)是一位美国画家和发明家。莫尔斯对单线电报系统的发明做出了贡献,并且他是摩尔斯电码的共同开发者。
[24] 在希腊神话中,泰坦和泰坦娜是乌拉诺斯和盖亚的孩子。他们是统治传说中的黄金时代(在奥林匹斯众神之前)的巨神。男性泰坦有科伊奥斯、海洋神、克里乌斯、克罗诺斯、赫利俄斯和亚普斯特斯,而女性泰坦娜有忒提斯、记忆女神、忒弥斯、提亚、雷亚和福玻斯。在一场被称为泰坦之战的战斗中,为了决定哪一代神将统治宇宙,奥林匹斯众神战胜了泰坦!
更多...
这是嵌套的决策控制结构
这是一个嵌套的多选决策结构。
这是一个嵌套的单选决策结构
这是一个嵌套的双选决策结构
此语句不受前一个决策控制结构的影响,也不影响下一个。
前一个和下一个决策控制结构受此语句影响
代码片段 1
代码片段 1
目的地在国内。检查重量并计算相应的运费。
目的地在国外。检查重量并计算相应的运费。
-5 < x ≤ 0
0 < x ≤ 6
6 < x ≤ 20
所有其他 x 的值
代码片段 1
这对语句执行 4 次,迫使用户输入 4 个数字。
这是程序中某种重复的部分。
这必须写 20 遍
嵌套循环
这是双重选择决策结构
这是后测试循环结构
这是双重选择决策结构
一个语句或语句块 1
一个语句或语句块 2
一个语句或语句块 1
一个语句或语句块 2
一个语句或语句块 1
此代码片段计算分母。
代码片段 1
数据输入阶段未进行验证。
数据输入验证未显示错误信息。
数据输入验证,只有一个错误信息。
数据输入验证,针对每种输入错误显示不同的错误信息。
数据输入阶段未进行验证
数据输入验证,只有一个错误信息
数据输入验证
代码片段 1
无验证的数据输入阶段。
数据输入和验证
数据输入和验证
数据输入和验证
这是一个正式的参数列表
这是一个实际的参数列表
这是一个实际的参数列表
默认情况下,Python 中的列表是通过引用传递的。
[16] ↩︎









浙公网安备 33010602011771号