书名:Python程序设计(第3版)
ISBN:978-7-115-28325-2
本书由人民邮电出版社发行数字版。版权所有,侵权必究。
您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。
我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。
如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。
• 著 [美] 约翰·策勒(John Zelle)
译 王海鹏
责任编辑 陈冀康
• 人民邮电出版社出版发行 北京市丰台区成寿寺路11号
邮编 100164 电子邮件 315@ptpress.com.cn
• 读者服务热线:(010)81055410
反盗版热线:(010)81055315
Simplified Chinese translation copyright ©2017 by Posts and Telecommunications Press
ALL RIGHTS RESERVED
Python Programming An Introduction to Computer Science, Third Edition by John M. Zelle.
Copyright ©2017 Franklin, Beedle & Associates Incorporated.
本书中文简体版由Franklin, Beedle & Associates公司授权人民邮电出版社出版。未经出版者书面许可,对本书的任何部分不得以任何方式或任何手段复制和传播。
版权所有,侵权必究。
本书是面向大学计算机科学专业的教材。本书以Python语言为工具,采用相当传统的方法,强调解决问题、设计和编程是计算机科学的核心技能。
全书共13章,此外,还包含两个附录。第1章到第5章介绍计算机与程序、编写简单程序、数字计算、对象和图形、字符串处理等基础知识。第6章到第8章介绍函数、判断结构、循环结构和布尔值等话题。第9章到第13章着重介绍一些较为高级的程序设计方法,包括模拟与设计、类、数据集合、面向对象设计、算法设计与递归等。附录部分给出了Python快速参考和术语表。每一章的末尾配有丰富的练习,包括复习问题、讨论和编程联系等多种形式,帮助读者巩固该章的知识和技能。
本书特色鲜明、示例生动有趣、内容易读易学,适合Python入门程序员阅读,也适合高校计算机专业的教师和学生参考。
当出版商第一次发给我这本书的草稿时,我立刻感到十分兴奋。它看起来像是Python教科书,但实际上是对编程技术的介绍,只是使用Python作为初学者的首选工具。这是我一直以来想象的Python在教育中最大的用途:不是作为唯一的语言,而是作为第一种语言,就像在艺术中一样,开始学习时用铅笔绘画,而不是立即画油画。
作者在本书前言中提到,Python作为第一种编程语言是接近理想的,因为它不是“玩具语言”。作为Python的创建者,我不想独占所有的功劳:Python源于ABC,这种语言在20世纪80年代初由阿姆斯特丹国家数学和计算机科学研究所(CWI)的Lambert Meertens、Leo Geurts等人设计,旨在教授程序设计。如果说我为他们的工作添加了什么东西,那就是让Python变成了一种非玩具语言,具有广泛的用户群、广泛的标准和大量的第三方应用程序模块。
我没有正式的教学经验,所以我可能没有资格来评判其教育效果。不过,作为一名具有将近30年经验的程序员,读过本书,我非常赞赏本书对困难概念的明确解释。我也喜欢书中许多好的练习和问题,既检查理解,又鼓励思考更深层次的问题。 恭喜本书读者!学习Python将得到很好的回报。我保证在这个过程中你会感到快乐,我希望你在成为专业的软件开发人员后,不要忘记你的第一种语言。
——Guido van Rossum,Python之父
本书旨在作为大学的一门计算课程的主要教材。它采用相当传统的方法,强调解决问题、设计和编程是计算机科学的核心技能。但是,这些思想利用非传统语言(即Python)来说明。在我的教学经验中,我发现许多学生很难掌握计算机科学和程序设计的基本概念。这个困难可以部分归咎于最常用于入门课程的语言和工具的复杂性。因此,这本教材只有一个总目标:尽可能简单地介绍基础计算机科学概念,但不是过于简单。使用Python是这个目标的核心。
传统的系统语言(如C++、Ada和Java)的发展是为了解决大规模编程中的问题,主要侧重于结构和纪律。它们不是为了易于编写中小型程序。最近脚本(有时称为“敏捷”)语言(如Python)的普及程度上升,这表明了一种替代方法。Python非常灵活,让实验变得容易。解决简单问题的方法简单而优雅。Python为新手程序员提供了一个很好的实验室。
Python具有一些特征,使其成为第一种编程语言的接近完美的选择。Python基本结构简单、干净、设计精良,使学生能够专注于算法思维和程序设计的主要技能,而不会陷入晦涩难解的语言细节。在Python中学习的概念可以直接传递给后续学习的系统语言(如C ++和Java)。但Python不是一种“玩具语言”,它是一种现实世界的生产语言,可以在几乎每个编程平台上免费提供,并且具有自己易于使用的集成编程环境。最好的是,Python让学习编程又变得有趣了。
虽然我使用Python作为语言,但Python教学并不是本书的重点。相反,Python用于说明适用于任何语言或计算环境的设计和编程的基本原理。在某些地方,我有意避免某些Python的功能和习惯用法,它们通常不会在其他语言中使用。市面上有很多关于Python的好书,本书旨在介绍计算。除了使用Python之外,本书还有其他一些特点,旨在使其成为计算机科学的平台。其中一些特点如下。
本书的第1版已经有些老旧,但它所采用的方法现在仍然有效,就像当时一样。
虽然基本原则并没有改变,但技术环境却变了。随着Python 3.0的发布,对原始资料的更新变得必要。第2版基本上与最初的版本相同,但更新使用了Python 3.0。本书中的每个程序示例几乎不得不针对新的Python来修改。此外,为了适应Python中的某些更改(特别是删除了字符串库),内容的顺序稍做了调整,在讨论字符串处理之前介绍了对象术语。这种变化有一个好的副作用,即更早介绍计算机图形学,以激发学生的兴趣。
第3版延续了更新课本以反映新技术的传统,同时保留了经过时间考验的方法来教授计算机科学的入门课程。这个版本的一个重要变化是消除了eval的大部分用法,并增加了其危险性的讨论。在连接越来越多的世界中,越早开始考虑计算机安全性越好。
本书添加了几个新的图形示例,在第4章到第12章中给出,以引入支持动画的图形库的新功能,包括简单的视频游戏开发。这使得最新的课本与大作业项目的类型保持一致,这些大作业常在现代的入门课程中布置。
在整个课本中还有一些较小的改动,其中包括:
为了保持简单的目标,我试图限制2门课不会涵盖的内容数量。不过,这里的内容可能比较多,典型的一学期入门课程也许不能涵盖。我的课程依次介绍了前12章中的几乎所有内容,尽管不一定深入介绍每个部分。第13章(“算法设计与递归”)中的一个或两个主题通常穿插在学期中的适当时候。
注意到不同的教师喜欢以不同的方式处理主题,我试图保持材料相对灵活。第1章~第4章(“计算机和程序”“编写简单程序”“数字计算”“对象和图形”)是必不可少的介绍,应该按顺序进行说明。字符串处理的第5章(“序列:字符串、列表和文件”)的初始部分也是基本的,但是稍后的主题(如字符串格式化和文件处理)可能会被延迟,直到后来需要。第6章~第8章(“定义函数”“判断结构”和“循环结构和布尔值”)设计为独立的,可以以任何顺序进行。关于设计方法的第9章~第12章是按顺序进行的,但是如果教师希望在各种设计技术之前介绍列表(数组),那么第11章(“数据集合”)中的内容可以很容易地提前。希望强调面向对象设计的教师不需要花费很多时间在第9章。第13章包含更多高级材料,可能会在最后介绍或穿插在整个课程的各个地方。
多年来,我教授CS1的方法受到了我读过并用于课堂的许多新教材的影响。我从这些书中学到的很多东西无疑已经融入了本书。有几位专家的方法非常重要,我觉得他们值得特别提及。A. K. Dewdney一直有一个诀窍,找出说明复杂问题的简单例子。我从中借鉴了一些,装上了Python的新腿。我也感谢Owen Astrachan和Cay Horstmann的精彩教科书。我在第4章介绍的图形库直接受到Horstmann设计的类似库的教学经验启发。我也从Nell Dale那里学到了很多关于教授计算机科学的知识,当时我是得克萨斯大学的研究生,很幸运地担任了助教。
许多人直接或间接地为本书做出了贡献。我也得到了沃特伯格学院的同事(和前同事)的很多帮助和鼓励:Lynn Olson在一开始就不动摇地支持,Josef Breutzmann提供了许多项目想法,Terry Letsche为第1版和第3版编写了PowerPoint幻灯片。
我要感谢以下阅读或评论第1版手稿的人:莫赫德州立大学的Rus May、北卡罗莱纳州立大学的Carolyn Miller、谷歌的Guido Van Rossum、加州州立大学(Chico)的Jim Sager、森特学院的Christine Shannon、罗彻斯特理工学院的Paul Tymann、亚利桑那大学的Suzanne Westbrook。我很感激首都大学的Dave Reed,他使用了第1版的早期版本,提供了无数有见地的建议,并与芝加哥大学的Jeffrey Cohen合作,为本版本提供了替代的章末练习。Ernie Ackermann在玛丽华盛顿学院试讲了第2版。第3版是由位于San Luis Obispo的加州理工大学的Theresa Migler和我的同事Terry Letsche在课堂上试讲的。David Bantz对草稿提供了反馈意见。感谢所有的宝贵意见和建议。
我也要感谢Franklin, Beedle and Associates的朋友,特别是Tom Sumner、Brenda Jones和Jaron Ayres,他们把我喜爱的项目变成一本真正的教科书。本版献给Jim Leisy作为纪念,他是Franklin, Beedle and Associates的创始人,在第3版正要付梓时意外过世。Jim是个了不起的人,兴趣非常广泛。正是他的远见、指导、不懈的热情和不断的激励,最终让我成为一名教科书作者,让这本书成功。
特别感谢所有我教过的学生,他们在教学方面给我很多教益。还要感谢沃特伯格学院批准我休假,支持我写书。最后但最重要的是,我要感谢我的妻子Elizabeth Bingham,她作为编辑、顾问和鼓舞士气者,在我写作期间容忍我。
几乎每个人都用过计算机。也许你玩过计算机游戏,或曾用计算机写文章、在线购物、听音乐,或通过社交媒体与朋友联系。计算机被用于预测天气、设计飞机、制作电影、经营企业、完成金融交易和控制工厂等。
你是否停下来想过,计算机到底是什么?一个设备如何能执行这么多不同的任务?学习计算机和计算机编程就从这些基本问题开始。
现代计算机可以被定义为“在可改变的程序的控制下,存储和操纵信息的机器”。该定义有两个关键要素。第一,计算机是用于操纵信息的设备。这意味着我们可以将信息放入计算机,它可以将信息转换为新的、有用的形式,然后输出或显示信息,让我们解释。
第二,计算机不是唯一能操纵信息的机器。当你用简单的计算器来加一组数字时,就在输入信息(数字),计算器就在处理信息,计算连续的总和,然后显示。另一个简单的例子是油泵。给油箱加油时,油泵利用某些输入:当前每升汽油的价格和来自传感器的信号,读取汽油流入汽车油箱的速率。油泵将这个输入转换为加了多少汽油和应付多少钱的信息。
我们不会将计算器或油泵看作完整的计算机,尽管这些设备的现代版本实际上可能包含嵌入式计算机。它们与计算机不同,它们被构建为执行单个特定任务。这就是定义的第二部分出现的地方:计算机在可改变的程序的控制下运行。这到底是什么意思?
“计算机程序”是一组详细的分步指令,告诉计算机确切地做什么。如果我们改变程序,计算机就会执行不同的动作序列,因而执行不同的任务。正是这种灵活性,让计算机在一个时刻是文字处理器,在下一个时刻是金融顾问,后来又变成一个街机游戏。机器保持不变,但控制机器的程序改变了。
每台计算机只是“执行”(运行)程序的机器。有许多不同种类的计算机。你可能熟悉Macintosh、PC、笔记本计算机、平板计算机和智能手机,但不论实际上还是理论上,都有数千种其他类型的计算机。计算机科学有一个了不起的发现,即认识到所有这些不同的计算机具有相同的力量,通过适当的编程,每台计算机基本上可以做任何其他计算机可以做的事情。在这个意义上说,放在你的办公桌上的PC实际上是一台通用机器。它可以做任何你想要它做的事,只要你能足够详细地描述要完成的任务。现在它是一台强大的机器!
你已经知道了计算的一个要点:“软件”(程序)主宰“硬件”(物理机器)。软件决定计算机可以做什么。没有软件,计算机只是昂贵的镇纸。创建软件的过程称为“编程”,这是本书的主要关注点。
计算机编程是一项具有挑战性的活动。良好的编程既要有全局观,又要注意细节。不是每个人都有天赋成为一流的程序员,正如不是每个人都具备成为专业运动员的技能。然而,几乎任何人都可以学习如何为计算机编程。只要有一点耐心和努力,本书将帮助你成为一名程序员。
学习编程有很多好理由。编程是计算机科学的一个基本组成部分,因此对所有立志成为计算机专业人员的人都很重要。但其他人也可以从编程经验中受益。计算机已经成为我们社会中的常见工具。要理解这个工具的优点和局限性,就需要理解编程。非程序员经常觉得他们是计算机的奴隶。然而,程序员是真正的控制者。如果你希望成为一个更聪明的计算机用户,本书就是为你准备的。
编程也有很多乐趣。这是一项智力活动,让人们通过有用的、有时非常漂亮的创作来表达自己。不管你信不信,许多人确实爱好编写计算机程序。编程也会培养有价值的问题解决技能,特别是将复杂系统分解为一些可理解的子系统及其交互,从而分析复杂系统的能力。
你可能知道,程序员有很大的市场需求。不少文科生已经将一些计算机编程课程作为一种有利可图的职业选择。计算机在当今的商业世界中如此常见,以至于理解计算机和编程的能力可能就会让你在竞争中占据优势,不论你是何种职业。灵感迸发时,你就准备好写出下一个杀手级应用程序了。
你可能会惊讶地得知,计算机科学不是研究计算机的。著名计算机科学家Edsger Dijkstra曾经说过,计算机之于计算机科学,正如望远镜之于天文学。计算机是计算机科学中的重要工具,但它本身不是研究的对象。由于计算机可以执行我们描述的任何过程,所以真正的问题是:“我们可以描述什么过程?”换句话说,计算机科学的根本问题就是“可以计算什么”。计算机科学家利用许多研究技术来回答这个问题。其中三种主要技术是设计、分析和实验。
证明某个问题可以解决的一种方式就是实际设计解决方案。也就是说,我们开发了一个逐步的过程,以实现期望的结果。计算机科学家称之为“算法”。这是一个奇特的词,基本上意味着“菜谱”。算法设计是计算机科学中最重要的方面之一。在本书中,你会看到设计和实现算法的技术。
设计有一个弱点,它只能回答“什么是可计算的”。如果可以设计一个算法,那么问题是可解的。然而,未能找到算法并不意味着问题是不可解的。这可能意味着我只是不够聪明,或者碰巧还没有找到正确的想法。这就是引入分析的原因。
分析是以数学方式检查算法和问题的过程。计算机科学家已经指出,一些看似简单的问题不能通过任何算法解决。另一些问题是“难解的”(intractable)。解决这些问题的算法需要太长时间,或者需要太多存储器,因而没有实际价值。算法分析是计算机科学的重要组成部分,在整本书中,我们将探讨一些基本原则。第13章有不可解决和难解问题的例子。
一些问题太复杂或定义不明确,无法分析。在这种情况下,计算机科学家就依靠实验。他们实际实现一些系统,然后研究结果的行为。即使在进行理论分析时,也经常需要实验来验证和完善分析。对于大多数问题,底线是能否构建一个可靠的工作系统。通常我们需要对系统进行经验性测试,以确定这个底线已经满足。当你开始编写自己的程序时,会有很多机会观察你的解决方案的表现。
我已经从设计、分析和评估算法的角度定义了计算机科学,这当然是该学科的核心。然而,当今计算机科学家参与广泛的活动,所有这些活动都在计算这把大伞之下。一些例子包括移动计算、网络、人机交互、人工智能、计算科学(使用强大的计算机来模拟科学过程)、数据库和数据挖掘、软件工程、网络和多媒体设计、音乐制作、管理信息系统和计算机安全。无论在何处进行计算,计算机科学的技能和知识都有应用。
你不必知道计算机工作的所有细节,也能成为一名成功的程序员,但了解基本原理将有助于掌握让程序运行所需的步骤。这有点像驾驶汽车。了解一点内燃机知识,有助于解释为什么必须做一些事情,如加油、点火、踩油门等。你可以通过记住要做什么来学习驾驶,但拥有更多知识会让整个过程更容易理解。让我们花一点时间来看看计算机的内部构造。
虽然不同计算机在具体细节上会显著不同,但在更高的层面上,所有现代数字计算机是非常相似的。图1.1展示了计算机的功能视图。中央处理单元(CPU)是机器的“大脑”。这是计算机执行所有基本操作的地方。CPU可以执行简单的算术运算,如两个数相加,也可以执行逻辑操作,如测试两个数是否相等。
图1.1 计算机的功能视图
存储器存储程序和数据。CPU只能直接访问存储在“主存储器”(称为RAM,即随机存取存储器)中的信息。主存储器速度快,但它也是易失性存储。也就是说,当电源关闭时,存储器中的信息会丢失。因此,还必须有一些辅助存储器,提供永久的存储。
在现代个人计算机中,主要的辅助存储器通常是内部的硬盘驱动器(HDD)或固态驱动器(SSD)。HDD将信息以磁模式存储在旋转磁盘上,而SSD使用称为闪存的电子电路。大多数计算机还支持可移动介质作为辅助存储器,如USB存储“棒”(也是一种形式的闪存)和DVD数字多功能光盘,后者以光学模式存储信息,由激光读取和写入。
人类通过输入和输出设备与计算机交互。你可能熟悉常见的设备,如键盘、鼠标和显示器(视频屏幕)。来自输入设备的信息由CPU处理,并可以被移动到主存储器或辅助存储器。类似地,需要显示信息时,CPU将它发送到一个或多个输出设备。
那么,你启动最喜欢的游戏或文字处理程序时,会发生什么?构成程序的指令从(更)持久的辅助存储器复制到计算机的主存储器中。一旦指令被加载,CPU就开始执行程序。
技术上,CPU遵循的过程称为“读取—执行循环”。从存储器取得第一条指令,解码以弄清楚它代表什么,并且执行适当的动作。然后,取得并解码和执行下一条指令。循环继续,指令接着指令。这确实是所有的计算机从你打开它直到再次关闭时做的事情:读取指令、解码、执行。这看起来不太令人兴奋,是吗?但计算机能以惊人的速度执行这个简单的指令流,每秒完成数十亿条指令。将足够多的简单指令以正确的方式放在一起,计算机完成了惊人的工作。
请记住,程序只是一系列指令,告诉计算机做什么。显然,我们需要用计算机可以理解的语言来提供这些指令。如果可以用我们的母语告诉计算机做什么,就像科幻电影中那样,当然很好。(“计算机,曲速引擎全速到达行星Alphalpha需要多长时间?”)计算机科学家在这个方向上取得了长足的进步。你可能熟悉Siri(Apple)、Google Now(Android)和Cortana(Microsoft)等技术。但是,所有认真使用过这种系统的人都可以证明,设计一个完全理解人类语言的计算机程序仍然是一个未解决的问题。
即使计算机可以理解我们,人类语言也不太适合描述复杂的算法。自然语言充满了模糊和不精确。例如,如果我说“I saw the man in the park with the telescope”,是我拥有望远镜,还是那个人拥有望远镜?谁在公园里?我们大多数时间都相互理解,因为所有人都拥有广泛的共同知识和经验。但即便如此,误解也是很常见的。
计算机科学家已经设计了一些符号,以准确无二义的方式来表示计算,从而绕过了这个问题。这些特殊符号称为编程语言。编程语言中的每个结构都有精确的形式(它的“语法”)和精确的含义(它的“语义”)。编程语言就像一种规则,用于编写计算机将遵循的指令。实际上,程序员通常将他们的程序称为“计算机代码”(computer code),用编程语言来编写算法的过程被称为“编码”(coding)。
Python是一种编程语言,它是我们在本书中使用的语言[1]。你可能已经听说过其他一些常用的语言,如C ++、Java、Javascript、Ruby、Perl、Scheme和BASIC。计算机科学家已经开发了成千上万种编程语言,而且语言本身随着时间演变,产生多个、有时非常不同的版本。虽然这些语言在许多细节上不同,但它们都有明确定义的、无二义的语法和语义。
上面提到的所有语言都是高级计算机语言的例子。虽然它们是精确的,但它们的设计目的是让人使用和理解。严格地说,计算机硬件只能理解一种非常低级的语言,称为“机器语言”。
假设我们希望让计算机对两个数求和。CPU实际执行的指令可能是这样的:
将内存位置2001的数加载到CPU中
将内存位置2002的数加载到CPU中
在CPU中对这两个数求和
将结果存储到位置2003
两个数求和似乎有很多工作,不是吗?实际上,它甚至比这更复杂,因为指令和数字以二进制符号表示(即0和1的序列)。
在Python这样的高级语言中,两个数求和可以更自然地表达为c = a + b。这让我们更容易理解,但我们需要一些方法,将高级语言翻译成计算机可以执行的机器语言。有两种方法可以做到这一点:高级语言可以被“编译”或“解释”。
“编译器”是一个复杂的计算机程序,它接受另一个以高级语言编写的程序,并将其翻译成以某个计算机的机器语言表达的等效程序。图1.2展示了编译过程的框图。高级程序被称为“源代码”,得到的“机器代码”是计算机可以直接执行的程序。图中的虚线表示机器代码的执行(也称为“运行程序”)。
图1.2 编译高级语言
“解释器”是一个程序,它模拟能理解高级语言的计算机。解释器不是将源程序翻译成机器语言的等效程序,而是根据需要一条一条地分析和执行源代码指令。图1.3展示了这个过程。
图1.3 解释高级语言
解释和编译之间的区别在于,编译是一次性翻译。一旦程序被编译,它可以重复运行而不需要编译器或源代码。在解释的情况下,每次程序运行时都需要解释器和源代码。编译的程序往往更快,因为翻译是一次完成的,但是解释语言让它们拥有更灵活的编程环境,因为程序可以交互式开发和运行。
翻译过程突出了高级语言对机器语言的另一个优点:可移植性。计算机的机器语言由特定CPU的设计者创建。每种类型的计算机都有自己的机器语言。笔记本计算机中的Intel i7处理器程序不能直接在智能手机的ARMv8 CPU上运行。不同的是,以高级语言编写的程序可以在许多不同种类的计算机上运行,只要存在合适的编译器或解释器(这只是另一个程序)。因此,我可以在我的笔记本计算机和平板计算机上运行完全相同的Python程序。尽管它们有不同的CPU,但都运行着Python解释器。
在你已了解了所有技术细节后,就可以开始享受Python的乐趣了。最终的目标是让计算机按我们的要求办事。为此,我们将编写控制机器内部计算过程的程序。你已经看到,这个过程中没有魔法,但编程的某些方面让人感觉像魔法。
计算机内部的计算过程就像一些魔法精灵,我们可以利用它们为我们工作。不幸的是,这些精灵只能理解一种非常神秘的语言,而我们不懂。我们需要一个友好的小仙子,能指导这些精灵实现我们的愿望。我们的小仙子是一个Python解释器。我们可以向Python解释器发出指令,并指导下面的精灵来执行我们的需求。我们通过一种特殊的法术和咒语(即Python)与小仙子沟通。开始学习Python的最好方法是将我们的小仙子放出瓶子,尝试一些法术。
对于大多数Python安装,你可以用交互模式启动Python解释器,这称为shell。shell允许你键入Python命令,然后显示执行它们的结果。启动shell的具体细节因不同安装而异。如果你使用来自www.python.org的PC或Mac的标准Python发行版,应该有一个名为IDLE的应用程序,它提供了Python shell,正如我们稍后会看到,它还可以帮助你创建和编辑自己的Python程序。本书的支持网站提供了在各种平台上安装和使用Python的信息。
当你第一次启动IDLE(或另一个Python shell),应该看到如下信息:
Python 3.4.3 (v3.4.3:9b73f1c3e601, Feb 24 2015, 22:43:06)
[MSC v.1600 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>>
确切的启动消息取决于你正在运行的Python版本和你正在使用的系统。重要的部分是最后一行。>>>是一个Python提示符,表示我们的小仙子(Python解释器)正在等待我们给它一个命令。在编程语言中,一个完整的命令称为语句。
下面是与Python shell交互的例子:
>>> print("Hello, World!")
Hello, World!
>>> print(2 + 3)
5
>>> print("2 + 3 =", 2 + 3)
2 + 3 = 5
这里,我尝试了三个使用Python的print语句的例子。第一个print语句要求Python显示文本短语Hello, World!。Python在下一行做出响应,打印出该短语。第二个print语句要求Python打印2与3之和。第三个print结合了这两个想法。Python打印出引号中的部分“2 + 3 =”,然后是2 + 3的结果,即5。
这种shell交互是在Python中尝试新东西的好方法。交互式会话的片段散布在本书中。如果你在示例中看到Python提示符>>>,这就告诉你正在展示交互式会话。启动自己的Python shell并尝试这些例子,是一个好主意。
通常,我们希望超越单行的代码片段,并执行整个语句序列。Python允许我们将一系列语句放在一起,创建一个全新的命令或函数。下面的例子创建了一个名为hello的新函数:
>>> def hello():
print("Hello")
print("Computers are fun!")
>>>
第一行告诉Python,我们正在定义一个新函数,命名为hello。接下来两行缩进,表明它们是hello函数的一部分。(注意:有些shell会在缩进行的开头打印省略号[“...”])。最后的空白行(通过按两次<Enter>键获得)让Python知道定义已完成,并且shell用另一个提示符进行响应。注意,键入定义并不会导致Python打印任何东西。我们告诉Python,当hello函数用作命令时应该发生什么,但实际上并没有要求Python执行它。
键入函数名称并跟上括号,函数就被调用了。下面是使用hello命令时发生的事情:
>>> hello()
Hello
Computers are fun!
>>>
你看到这完成了什么?hello函数定义中的两个print语句按顺序执行了。
你可能对定义中的括号和hello的使用感到好奇。命令可以有可变部分,称为参数(也称为变元),放在括号中。让我们看一个使用参数、自定义问候语的例子。先是定义:
>>> def greet(person):
print("Hello", person)
print("How are you?")
现在我们可以使用定制的问候。
>>> greet("John")
Hello John
How are you?
>>> greet("Emily")
Hello Emily
How are you?
>>>
你能看到这里发生了什么吗?使用greet时,我们可以发送不同的名称,从而自定义结果。你可能也注意到,这看起来类似于之前的print语句。在Python中,print是一个内置函数的例子。当我们调用print函数时,括号中的参数告诉函数要打印什么。
我们将在后面详细讨论参数。目前重要的是要记住,执行一个函数时,括号必须包含在函数名之后。即使没有给出参数也是如此。例如,你可以使用print而不使用任何参数,创建一个空白的输出行。
>>> print()
>>>
但是如果你只键入函数的名称,省略括号,函数将不会真正执行。相反,交互式Python会话将显示一些输出,表明名称所引用的函数,如下面的交互所示:
>>> greet
<function greet at 0x8393aec>
>>> print
<built-in function print>
有趣的文本0x8393aec是在计算机存储器中的位置(地址),其中恰好存储了greet函数的定义。如果你在自己的计算机上尝试,几乎肯定会看到不同的地址。
将函数交互式地输入到Python shell中,像我们的hello和greet示例那样,这存在一个问题:当我们退出shell时,定义会丢失。如果我们下次希望再次使用它们,必须重新键入。程序的创建通常是将定义写入独立的文件,称为“模块”或“脚本”。此文件保存在辅助存储器中,所以可以反复使用。
模块文件只是一个文本文件,你可以用任何应用程序来编辑文本,例如记事本或文字处理程序,只要将程序保存为“纯文本”文件即可。有一种特殊类型的应用称为集成开发环境(IDE),它们简化了这个过程。IDE专门设计用于帮助程序员编写程序,包括自动缩进、颜色高亮显示和交互式开发等功能。IDLE是一个很好的例子。到目前为止,我们只将IDLE作为一个Python shell,但它实际上是一个简单却完整的开发环境[2]。
让我们编写并运行一个完整的程序,从而说明模块文件的使用。我们的程序将探索一个被称为混沌(chaos)的数学概念。要将此程序键入IDLE,应选择File/New File菜单选项。这将打开一个空白(非shell)窗口,你可以在其中键入程序。下面是程序的Python代码:
# File: chaos.py
# A simple program illustrating chaotic behavior.
def main():
print("This program illustrates a chaotic function")
x = eval(input("Enter a number between 0 and 1: "))
for i in range(10):
x = 3.9 * x * (1 - x)
print(x)
main()
键入它之后,从菜单中选择File/Save,并保存为chaos.py。扩展名.py表示这是一个Python模块。在保存程序时要小心。有时IDLE默认会在系统范围的Python文件夹中启动。要确保导航到你保存自己文件的文件夹。我建议将所有Python程序放在一个专用的文件夹中,放在你自己的个人文档目录中。
此时,你可能正试图理解刚刚输入的内容。你可以看到,这个特定的例子包含了几行代码,定义了一个新函数main。(程序通常放在一个名为main的函数中。)文件的最后一行是调用此函数的命令。如果你不明白main实际上做了什么,也不要担心,我们将在下一节中讨论它。这里的要点在于,一旦我们将一个程序保存在这样的模块文件中,就可以随时运行它。
我们的程序能以许多不同的方式运行,这取决于你使用的实际操作系统和编程环境。如果你使用的是窗口系统,则可以通过单击(或双击)模块文件的图标来运行Python程序。在命令行情况下,可以键入像python chaos.py这样的命令。使用IDLE时,只需从模块窗口菜单中选择Run/Run Module即可运行程序。按下<F5>键是该操作的方便快捷方式。
IDLE运行程序时,控制将切换到shell窗口。下面是看起来的样子:
>>> ======================= RESTART =======================
>>>
This program illustrates a chaotic function
Enter a number between 0 and 1: .25
0.73125
0.76644140625
0.6981350104385375
0.8218958187902304
0.5708940191969317
0.9553987483642099
0.166186721954413
0.5404179120617926
0.9686289302998042
0.11850901017563877
>>>
第一行是来自IDLE的通知,表明shell已重新启动。IDLE在每次运行程序时都会这样做,这样程序就运行在一个干净的环境中。Python然后从上至下逐行运行该模块。这就像我们在交互式Python提示符下逐行键入它们一样。模块中的def会导致Python创建main函数。这个模块的最后一行导致Python调用main函数,从而运行我们的程序。正在运行的程序要求用户输入一个介于0和1之间的数字(在这个例子中,我键入“.25”),然后打印出10个数字的序列。
如果浏览计算机上的文件,你可能会注意到,Python有时会在存储模块文件的文件夹中创建另一个名为pycache的文件夹。这里是Python存储伴随文件的地方,伴随文件的扩展名为.pyc。在本例中,Python可能会创建另一个名为chaos.pyc的文件。这是Python解释器使用的中间文件。从技术上讲,Python采用混合编译/解释的过程。模块文件中的Python源代码被编译为较原始的指令,称为字节代码。然后解释这个字节代码(.pyc)。如果有.pyc文件可用,则第二次运行模块就会更快。但是,如果要节省磁盘空间,你可以删除字节代码文件。Python会根据需要自动重新创建它们。
在IDLE下运行模块,会将程序加载到shell窗口中。你可以要求Python执行main命令,从而再次运行该程序。只需在shell提示符下键入命令。继续我们的例子,下面是我们重新运行程序时它的样子,以“.26”作为输入:
>>> main()
This program illustrates a chaotic function
Enter a number between 0 and 1: .26
0.75036
0.73054749456
0.767706625733
0.6954993339
0.825942040734
0.560670965721
0.960644232282
0.147446875935
0.490254549376
0.974629602149
>>>
chaos程序的输出可能看起来不太令人兴奋,但它说明了物理学家和数学家已知的一个非常有趣的现象。让我们逐行来看这个程序,看看它做了什么。不要担心不能马上理解每个细节,我们将在下一章重新探讨所有这些想法。
程序的前两行以#字符开头:
# File: chaos.py
# A simple program illustrating chaotic behavior.
这些行称为“注释”。它们是为程序的人类读者编写的,会被Python忽略。Python解释器总是跳过从井号(#)到行末之间的所有文本。
程序的下一行开始定义一个名为main的函数:
def main():
严格地说,不需要创建main函数。因为模块的代码行在加载时会被执行,所以我们可以在没有这个定义的情况下编写我们的程序。也就是说,模块可能看起来像下面这样:
# File: chaos.py
# A simple program illustrating chaotic behavior.
print("This program illustrates a chaotic function")
x = eval(input("Enter a number between 0 and 1: "))
for i in range(10):
x = 3.9 * x * (1 - x)
print(x)
这个版本更短一些,但惯例是将包含程序的指令放在main函数内部。上面展示了这种方法的一个直接好处:它允许我们通过调用main()来运行程序。我们不必重新启动Python shell就能再次运行它,这在没有main的情况下是不行的。
main内部的第一行是程序真正的开始。
print("This program illustrates a chaotic function")
这行导致Python打印一个消息,在程序运行时介绍它自己。
看看程序的下一行:
x = eval(input("Enter a number between 0 and 1: "))
这里的x是变量的示例。变量为值提供了一个名称,以便我们在程序的其他位置引用它。
整行是一个语句,从用户那里获得一些输入。这一行内容有点多,我们将在下一章讨论它的细节。现在,你只需要知道它完成了什么。当Python遇到该语句时,它显示引号内的消息“Enter a number between 0 and 1:”并暂停,等待用户在键盘上键入内容,然后按<Enter>键。随后用户键入的值保存为变量x。在上面显示的第一个例子中,用户输入了“.25”,它成为x的值。
下一个语句是循环的示例。
for i in range(10):
循环是一种策略,它告诉Python重复做同样的事情。这个特定的循环说要做某事10次。在循环头下缩进的几行,是要执行10次的语句。它们构成了循环体。
x = 3.9 * x * (1 - x)
print(x)
循环的效果完全一样,就像我们将循环体写了10次:
x = 3.9 * x * (1 - x)
print(x)
x = 3.9 * x * (1 - x)
print(x)
x = 3.9 * x * (1 - x)
print(x)
x = 3.9 * x * (1 - x)
print(x)
x = 3.9 * x * (1 - x)
print(x)
x = 3.9 * x * (1 - x)
print(x)
x = 3.9 * x * (1 - x)
print(x)
x = 3.9 * x * (1 - x)
print(x)
x = 3.9 * x * (1 - x)
print(x)
x = 3.9 * x * (1 - x)
print(x)
显然,使用循环为程序员省却了很多麻烦。
但是这些语句究竟做了什么?第一句执行了计算:
x = 3.9 * x * (1 - x)
这被称为“赋值”语句。“=”右侧的部分是一个数学表达式。Python使用“*”字符表示乘法。回想一下,x的值是0.25(来自上面的input)。计算的值为3.9(0.25)(1 – 0.25),即0.73125。一旦计算出右侧的值,它就被保存为(或赋值给)出现在“=”左侧的变量,在这个例子中是x。x的新值(0.73125)替换了旧值(0.25)。
循环体中的第二行是我们之前遇到的一种语句类型,即print语句。
print(x)
Python执行此语句时,屏幕上将显示x的当前值。所以第一个输出的数是0.73125。
记住,循环要执行10次。打印x的值后,循环的两个语句再次执行。
x = 3.9 * x * (1 - x)
print(x)
当然,现在x的值为0.73125,所以公式计算新的x值为3.9(0.73125)(1 – 0.73125),它是0.76644140625。
你能看到每次循环时如何用x的当前值来计算一个新值吗?这是示例运行中数字的来源。你可以针对一个不同的输入值(例如0.5)尝试执行程序的步骤,然后用Python运行该程序,看看你模拟计算机的情况。
我在前面说过,chaos程序展示了一个有趣的现象。满屏幕的数字哪里有趣?如果你自己尝试这个程序会发现,无论从什么数字开始,结果总是相似的:程序吐出10个似乎随机的数字,在0和1之间。随着程序运行,x的值似乎跳来跳去,好吧,像混沌一样。
由该程序计算的函数具有一般形式k(x)(1–x),k在这个例子中是3.9。这被称为逻辑函数。它模拟某些类型的不稳定电子电路,并且有时也在限制条件下模拟群体变化。重复应用逻辑函数可以产生混沌。虽然我们的程序有一个明确的底层行为,但输出似乎不可预测。
混沌函数有一个有趣的属性,即随着公式被重复应用,初始值的非常小的差异可以导致结果的巨大差异。你可以在chaos程序中看到这一点,只需输入稍微不同的数字。以下是修改后的程序的输出,显示了初始值为0.25和0.26的结果:
input 0.25 0.26
---------------------------
0.731250 0.750360
0.766441 0.730547
0.698135 0.767707
0.821896 0.695499
0.570894 0.825942
0.955399 0.560671
0.166187 0.960644
0.540418 0.147447
0.968629 0.490255
0.118509 0.974630
使用非常相似的起始值,输出在几个迭代中保持相似,然后就显著不同了。大约到第五次迭代,两个模型之间就似乎没有任何关系了。
我们的chaos程序的这两个特征,即显然不可预测性和对初始值的极端敏感性,是混沌行为的标志。混沌对计算机科学有重要的影响。事实证明,在现实世界中,我们可能希望用计算机建模和预测的许多现象就是这种混沌行为。你可能听说过所谓的蝴蝶效应。用于模拟和预测天气模式的计算机模型是如此敏感,以至于一只蝴蝶在新泽西拍打翅膀,可能会影响伊利诺州皮奥里亚(Peoria)是否下雨的预测。
很可能即使有完美的计算机建模,我们也永远不能准确地测量已有的天气条件,从而提前几天预测天气。测量就是不够精确,不能让预测在较长时间内准确。
如你所见,这个小程序给计算机用户上了有价值的一课。尽管计算机如此神奇,但它们给出的结果只是与程序所基于的数学模型一样有用。计算机可能由于程序错误而给出不正确的结果,但如果模型错误或初始输入不够精确,即使正确的程序也可能产生错误的结果。
本章介绍了计算机、计算机科学和编程。下面是一些关键概念的小结。
1.计算机科学是计算机的研究。
2.CPU是计算机的“大脑”。
3.辅助存储器也称为RAM。
4.计算机当前正在处理的所有信息都存储在主存储器中。
5.语言的语法是它的意思,语义是它的形式。
6.函数定义是定义新命令的语句序列。
7.编程环境是指程序员工作的地方。
8.变量用于给一个值赋予一个名称,这样它就可以在其他地方被引用。
9.循环用于跳过程序的一部分。
10.混沌函数不能由计算机计算。
1.计算机科学的根本问题是 。
a.计算机的计算速度有多快
b.可以计算什么
c.什么是最有效的编程语言
d.程序员可以赚多少钱
2.算法类似于 。
a.报纸
b.捕蝇草
c.鼓
d.菜谱
3.一个问题是难解的,如果 。
a.你不能反转其解决方案
b.涉及拖拉机
c.它有很多解决方案
d.解决它不实际
4.以下 项不是辅助存储器。
a.RAM
b.硬盘驱动器
c.USB闪存驱动器
d.DVD
5.设计来让人类使用和理解的计算机语言是 。
a.自然语言
b.高级计算机语言
c.机器语言
d.提取—执行语言
6.语句是 。
a.机器语言的翻译
b.完整的计算机命令
c.问题的精确描述
d.算法的一部分
7.编译器和解释器之间的一个区别是 。
a.编译器是一个程序
b.使用编译器将高级语言翻译成机器语言
c.在程序翻译之后不再需要编译器
d.编译器处理源代码
8.按照惯例,程序的语句通常放在一个函数中,该函数名为 。
a.import
b.main
c.program
d.IDLE
9.关于注释,以下不正确的是 。
a.它们让程序更有效率
b.它们是为人类读者
c.它们被Python忽略
d.在Python中,它们以井号(#)开头
10.函数定义的括号中列出的项被称为 。
a.括号
b.参数
c.变元
d.b和c项都是正确的
1.比较并对比本章中的以下概念对。
a.硬件与软件
b.算法与程序
c.编程语言与自然语言
d.高级语言与机器语言
e.解释器与编译器
f.语法与语义
2.列出图1.1中计算机的5个基本功能单元,并用你自己的话并解释它们的作用。
3.写一个制作花生酱和果冻三明治(或其他日常活动)的详细算法。你应该假设正在与一个概念上能够完成该任务,但从来没有实际做过的人交谈。例如,你可能告诉一个小孩子怎么做。
4.正如你将在后续章节中学到的,存储在计算机中的许多数字不是精确的值,而是接近的近似值。例如,值0.1可能存储为0.10000000000000000555。通常,这样小的差异不是问题。然而,考虑到你在第1章中学到的混沌行为,你应该意识到在某些情况下需要谨慎。你能想到这可能是一个问题的例子吗?请说明。
5.使用0.15作为输入值,手动追踪第1.6节中的chaos程序。显示结果的输出序列。
1.启动交互式Python会话,并尝试键入以下每个命令。写下你看到的结果。
a.print("Hello, world!")
b.print("Hello", "world!")
c.print(3)
d.print(3.0)
e.print(2 + 3)
f.print(2.0 + 3.0)
g.print("2" + "3")
h.print("2 + 3 =", 2 + 3)
i.print(2 * 3)
j.print(2 ** 3)
k.print(7 / 3)
l.print(7 // 3)
2.输入并运行第1.6节中的chaos
程序。尝试使用各种输入值,观察它在本章中描述的功能。
3.修改chaos
程序,使用2.0代替3.9作为逻辑函数中的乘数。你修改的代码行应该像下面这样:
x = 2.0 * x * (1 - x)
用各种输入值运行该程序,并将结果与从原始程序获得的结果进行比较。写一小段话,描述你在两个版本的行为中观察到的所有差异。
4.修改chaos
程序,让它打印出20个值,而不是10个。
5.修改chaos
程序,让打印值的数量由用户确定。你将必须在程序顶部附近添加一行,从用户获取另一个值:
n = eval(input("How many numbers should I print? "))
然后,你需要更改循环,使用n代替具体的数字。
6.在chaos
程序中执行的计算,可以用代数等价的多种方式来编写。为以下每种计算方式编写一个程序版本。让你修改的程序打印出100次迭代的计算,并比较相同输入的运行结果。
a.3.9 * x * (1 - x)
b.3.9 * (x - x * x)
c.3.9 * x - 3.9 * x * x
请解释这个实验的结果。提示:参见上面的讨论问题4。
7.(高级)修改chaos
程序,让它接受两个输入,然后打印一个包含两列的表,类似第1.8节中显示的表。(注意:你可能无法让列排得与示例中一样好,第5章将讨论如何使用固定小数位数打印数字。)
[1] 本书的这个版本使用Python 3.4版本开发和测试。Python 3.5现在可用。如果你的计算机上安装了早期版本的Python,则应升级到最新的稳定版3.x,以便尝试这些例子。
[2] 事实上,IDLE代表Integrated DeveLopment Environment。多出来的“L”是对Eric Idle的致敬,因为Monty Python的名望。
正如你在上一章中看到的,运行已经编写的程序很容易。较难的部分实际上是先得到一个程序。计算机是非常实在的,必须告诉它们要做什么,直至最后的细节。编写大型程序是一项艰巨的挑战。如果没有系统的方法,几乎是不可能的。
创建程序的过程通常被分成几个阶段,依据是每个阶段中产生的信息。简而言之,你应该做以下工作。
分析问题 确定要解决的问题是什么。尝试尽可能多地了解它。除非真的知道问题是什么,否则就不能开始解决它。
确定规格说明 准确描述程序将做什么。此时,你不必担心程序“怎么做”,而是要确定它“做什么”。对于简单程序,这包括仔细描述程序的输入和输出是什么以及它们的相互关系。
创建设计 规划程序的总体结构。这是描述程序怎么做的地方。主要任务是设计算法来满足规格说明。
实现设计 将设计翻译成计算机语言并放入计算机。在本书中,我们将算法实现为Python程序。
测试/调试程序 试用你的程序,看看它是否按预期工作。如果有任何错误(通常称为“缺陷”),那么你应该回去修复它们。定位和修复错误的过程称为“调试”程序。在调试阶段,你的目标是找到错误,所以应该尝试你能想到的“打破”程序的一切可能。记住这句老格言:“没有什么能防住人犯傻,因为傻子太聪明了。”
维护程序 继续根据用户的需求开发该程序。大多数程序从来没有真正完成,它们在多年的使用中不断演进。
让我们通过一个真实世界的简单例子,来体验软件开发过程的步骤,其中涉及一个虚构的计算机科学学生Susan Computewell。
Susan正在德国学习一年。她对语言没有任何问题,因为她能流利地使用许多语言(包括Python)。她的问题是,很难在早上弄清楚温度从而知道当天该穿什么衣服。Susan每天早上听天气报告,但温度以摄氏度给出,她习惯了华氏度。
幸运的是,Susan有办法解决这个问题。作为计算机科学专业的学生,她去任何地方总是带着她的笔记本计算机。她认为计算机程序可能会帮助她。
Susan开始分析她的问题。在这个例子中,问题很清楚:无线电广播员用摄氏度报气温,但Susan只能理解华氏温度。
接下来,Susan考虑可能帮助她的程序的规格说明。输入应该是什么?她决定程序将允许她输入摄氏温度。输出呢?程序将显示转换后的华氏温度。现在她需要指定输出与输入的确切关系。
苏珊快速估算了一下。她知道0摄氏度(冰点)等于32华氏度,100摄氏度(沸点)等于212华氏度。有了这个信息,她计算出华氏度与摄氏度的比率为(212−32)/(100−0) = (180/100) = 9/5。使用F表示华氏温度,C表示摄氏温度,转换公式的形式为F = (9/5)C + k,其中k为某个常数。代入0和32分别作为C和F,Susan立即得到k = 32。所以最后的关系公式是F = (9/5)C + 32。这作为规格说明似乎足够了。
请注意,这描述了能够解决这个问题的许多可能程序中的一个。如果Susan有人工智能(AI)领域的背景,她可能会考虑写一个程序,用语音识别算法实际收听收音机播音员,获得当前的温度。对于输出,她可以让计算机控制机器人进入她的衣柜,并根据转换后的温度选择适当的服装。这将是一个更有野心的项目,一点也不夸张!
当然,机器人程序也会解决问题分析中识别的问题。规格说明的目的,是准确地决定这个特定的程序要做什么,从而解决一个问题。Susan知道,最好是先弄清楚她希望构建什么,而不是一头钻进去开始编程。
Susan现在准备为她的问题设计一个算法。她马上意识到这是一个简单算法,遵循标准模式“输入、处理、输出”(IPO)。她的程序将提示用户输入一些信息(摄氏温度),处理它,产生华氏温度,然后在计算机屏幕上显示结果,作为输出。
Susan可以用一种计算机语言来写她的算法。然而,正式将它写出来需要相当的精度,这常常会扼杀开发算法的创造性过程。作为替代,她用“伪代码”编写算法。伪代码只是精确的英语,描述了程序做的事。这意味着既可以交流算法,又不必让大脑承担额外的开销,正确写出某种特定编程语言的细节。
下面是Susan的完整算法:
输入摄氏度温度(称为celsius)
计算华氏度为(9/5)celsius + 32
输出华氏度
下一步是将此设计转换为Python程序。这很直接,因为算法的每一行都变成了相应的Python代码行。
# convert.py
# A program to convert Celsius temps to Fahrenheit
# by: Susan Computewell
def main():
celsius = eval(input("What is the Celsius temperature? "))
fahrenheit = 9/5 * celsius + 32
print("The temperature is", fahrenheit, "degrees Fahrenheit.")
main()
看看你是否能弄清楚这个程序的每一行做了什么。如果一些部分不是很清楚,也不要担心,下一节将详细讨论。
完成程序后,Susan测试它,看看它工作得如何。她使用她知道正确答案的输入。下面是两个测试的输出:
What is the Celsius temperature? 0
The temperature is 32.0 degrees Fahrenheit.
What is the Celsius temperature? 100
The temperature is 212.0 degrees Fahrenheit.
你可以看到,Susan用值0和100来测试她的程序。看起来不错,她对解决方案感到满意。她特别高兴的是,似乎没有必要调试(这很不寻常)。
既然已经知道了编程过程,你就“几乎”准备好开始自己编写程序了。在此之前,你需要更完整的基础,了解Python的基本知识。接下来的几节将讨论一些技术细节,这对编写正确程序至关重要。这种材料看起来有点乏味,但你必须掌握这些基础,然后再进入更有趣的领域。
你已经看到,名称是编程的重要组成部分。我们为模块命名(例如convert),也为模块中的函数命名(例如main)。变量用于为值命名(例如celsius和fahrenheit)。从技术上讲,所有这些名称都称为“标识符”。Python对标识符的构成有一些规则。每个标识符必须以字母或下划线(“_”字符)开头,后跟字母、数字或下划线的任意序列。这意味着单个标识符不能包含任何空格。
根据上述规则,以下都是Python中的合法名称:
x
celsius
spam
spam2
SpamAndEggs
Spam_and_Eggs
标识符区分大小写,因此对Python来说,spam、Spam、sPam和SPAM是不同的名称。在大多数情况下,程序员可以自由选择符合这些规则的任何名称。好的程序员总是试图选择一些名字,它们能描述被命名的东西。
需要注意一件重要的事情:一些标识符是Python本身的一部分。这些名称称为“保留字”或“关键字”,不能用作普通标识符。Python关键字的完整列表如表2.1所列。
表2.1 Python关键字
False | class | finally | is | return |
None | continue | for | lambda | try |
True | def | from | nonlocal | while |
and | del | global | not | with |
as | elif | if | or | yield |
assert | else | import | pass | |
break | except | in | raise |
Python还包括相当多的内置函数,例如我们用过的print函数。虽然在技术上可以将内置的函数名称标识符用于其他目的,但这通常是一个“非常糟糕”的主意。例如,如果你重新定义print的含义,那么就无法再打印信息。你也会让所有阅读程序的Python程序员感到非常困惑,他们预期print指的是内置函数。内置函数的完整列表可在附录A中找到。
程序操作数据。到目前为止,我们已经在示例程序中看到了数字和文本两种不同类型的数据。我们将在后面的章节中详细讨论这些不同的数据类型。现在,你只需要记住,所有的数据必须以一些数字格式存储在计算机上,不同类型的数据以不同的方式存储。
产生或计算新数据值的程序代码片段称为“表达式”。最简单的表达式是字面量。字面量用于表示特定值。在chaos.py中,你可以找到数字3.9和1。convert.py程序包含9、5和32。这些都是数字字面量的例子,它们的含义显而易见:32就是代表32(数字32)。
我们的程序还以一些简单的方式处理文本数据。计算机科学家将文本数据称为“字符串”。你可以将字符串视为可打印字符的序列。Python中通过将字符括在引号("")中来表示字符串字面量。如果你回头看看我们的示例程序,可以发现一些字符串字面量,例如"Hello"和"Enter a number between 0 and 1:"。这些字面量产生的字符串包含引号内的字符。请注意,引号本身不是字符串的一部分。它们只是告诉Python创建一个字符串的机制。
将表达式转换为基础数据类型的过程称为“求值”。在Python shell中键入表达式时,shell会计算表达式并打印出结果的文本表示。请考虑以下简短的交互:
>>> 32
32
>>> "Hello"
'Hello'
>>> "32"
'32'
请注意,当shell显示字符串的值时,它将字符序列放在单引号中。这样让我们知道该值实际上是文本而不是数字(或其他数据类型)。在最后一次交互中,我们看到表达式"32"产生一个字符串,而不是一个数字。在这种情况下,Python实际上是存储字符“3”和“2”,而不是数字32的表示。如果你现在不太明白,不要太担心。我们在后面的章节中讨论这些数据类型时,你的理解就会变得更加清晰。
一个简单的标识符也可以是一个表达式。我们使用标识符作为变量来给名字赋值。当标识符作为表达式出现时,它的值会被取出,作为表达式的结果。下面是与Python解释器的交互,展示了变量作为表达式:
>>> x = 5
>>> x
5
>>> print(x)
5
>>> print(spam)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'spam' is not defined
首先,变量x被赋值为5(使用数字字面量5)。在第二行交互中,我们要求Python对表达式x求值。作为响应,Python shell打印出5,这是刚才赋给x的值。当然,如果我们明确要求Python用print语句打印x,也会得到相同的结果。最后一个交互展示了如果尝试使用未赋值的变量,会发生什么。Python找不到值,所以它报告NameError。这说明没有该名称的值。这里的要点是,变量总是必须赋一个值,然后才能在表达式中使用。
较复杂、较有趣的表达式可以通过组合较简单的表达式和操作符来构造。对于数字,Python提供了一组标准的数学运算:加法、减法、乘法、除法和乘方。相应的Python运算符为“+”“-”“*”“/”和“**”。下面是一些来自chaos.py和convert.py的复杂表达式的例子:
3.9 * x * (1 - x)
9/5 * celsius + 32
空格在表达式中没有作用。最后一个表达式如果写成9/5*celsius+32,结果完全相同。通常,在表达式中加一些空格让它更容易阅读,是个好方法。
Python的数学运算符遵循的优先级和结合律,与你在数学课上学到的相同,包括使用括号来改变求值的顺序。在自己的程序中构建复杂表达式应该没什么困难。请记住,只有圆括号在数字表达式中是允许的。如果需要,可以嵌套使用它们,创建如下的表达式:
((x1 - x2) / 2*n) + (spam / k**3)
顺便说一句,Python还提供了字符串的运算符。例如,可以“加”字符串。
>>> "Bat" + "man"
'Batman'
这被称为“连接”。如你所见,效果是创建一个新的字符串,把两个字符串“粘”在一起。你将在第5章看到更多的字符串操作。
既然有了基本的构建块(标识符和表达式),你就可以更完整地描述各种Python语句。 你已经知道信息可以使用Python的内置函数print在屏幕上显示。到目前为止,我们已经看了几个例子,但我还没有详细解释打印功能。像所有的编程语言一样,Python对每个语句的语法(形式)和语义(意义)有一套精确的规则。计算机科学家已经开发了复杂的符号表示法,称为“元语言”,用于描述编程语言。在本书中,我们将依靠一个简单的模板符号表示法来说明各种语句的语法。
因为print是一个内置函数,所以print语句与任何其他函数调用具有相同的一般形式。我们键入函数名print,后面带上括号中列出的参数。下面是用我们的模板符号时print语句看起来的样子:
print(<expr>, <expr>, ..., <expr>)
print()
这两个模板展示了两种形式的print语句。第一个表示print语句可以包含函数名print,后面带上带括号的表达式序列,用逗号分隔。模板中的尖括号符号(<>)用于表示由Python代码的其他片段填充的“槽”。括号内的名称表示缺少什么,expr表示一个表达式。省略号(“...”)表示不确定的序列(在这个例子中是表达式)。你实际上不会输入圆点。第二个版本的print语句表明,不打印任何表达式的print也是合法的。
就语义而言,print语句以文本形式显示信息。所有提供的表达式都从左到右求值,结果值以从左到右的方式显示在输出行上。默认情况下,在显示的值之间放置一个空格字符。作为示例,下面print语句的序列:
print(3+4)
print(3, 4, 3 + 4)
print()
print("The answer is", 3 + 4)
产生的输出为:
7
3 4 7
The answer is 7
最后一个语句说明了,字符串字面量表达式如何经常在print语句使用,作为标记输出的方便方法。
注意,连续的print语句通常显示在屏幕的不同行上。空print(无参数)生成空行输出。在背后,真正发生的是,在打印所有提供的表达式之后,print函数自动附加某种结束文本。默认情况下,结束文本是表示行结束的特殊标记字符(表示为“\n”)。我们可以通过包含一个附加参数显式地覆盖这个默认值,从而改变这种行为。这里使用命名参数的特殊语法,或称为“关键字”参数。
包含指定结束文本的关键字参数的print语句的模板如下:
print(<expr>, <expr>, ..., <expr>, end="\n")
命名参数的关键字是end,它使用“=”符号赋值,类似于变量赋值。注意,在模板中我已经显示其默认值,即行末字符。这是一种标准方式,用于显示在未明确指定某个其他值时,关键字参数具有的值。
print语句中的end参数有一个常见用法,即允许多个print构建单行输出。例如:
print("The answer is", end=" ")
print(3 + 4)
产生单行输出:
The answer is 7
注意,第一个print语句的输出如何以空格(" ")而不是行末字符结束,第二个语句的输出紧跟在空格之后。
Python中最重要的语句之一是赋值语句。我们在前面的例子中已经看到了一些。
基本赋值语句具有以下形式:
<variable> = <expr>
这里variable是一个标识符,expr是一个表达式。赋值的语义是,右侧的表达式被求值,然后产生的值与左侧命名的变量相关联。
下面是我们已经看到的一些赋值:
x = 3.9 * x * (1 - x)
fahrenheit = 9 / 5 * celsius + 32
x = 5
变量可以多次赋值。它总是保留最新赋的值。下面的交互式Python会话展示了这一点:
>>> myVar = 0
>>> myVar
0
>>> myVar = 7
>>> myVar
7
>>> myVar = myVar + 1
>>> myVar
8
最后一个赋值语句展示了如何使用变量的当前值来更新它的值。在这个例子中,我只是对以前的值加1。第1章的chaos.py程序做了类似的事情,但更复杂一些。记住,变量的值可以改变,这就是为什么它们被称为变量的原因。
有时,将变量看作计算机内存中的一种命名的存储位置是有帮助的,我们可以在其中放入一个值。当变量更改时,旧值将被删除,并写入一个新值。图2.1展示了用这个模型来描绘x = x + 1的效果。这正是赋值在某些计算机语言中工作的方式。这也是查看赋值效果的一种非常简单的方式,你会在整本书中看到类似这样的图片。
图2.1 x = x + 1的视图,变量就像盒子
Python赋值语句实际上与“变量盒子”模型略有不同。在Python中,值可能最终放在内存中的任何位置,而变量用于引用它们。对变量赋值就像把一个黄色小粘贴便签放在值上,并说“这是x”。图2.2给出了一个更准确的Python赋值的效果。箭头用于显示变量引用的值。请注意,旧值不会被新值擦除,变量只需切换到引用新值。效果就像将粘贴便签从一个对象移动到另一个对象一样。这是赋值在Python中实际工作的方式,所以你会看到这样一些粘贴便签样式的图片散布在本书中。
图2.2 x = x + 1的(Python)视图,变量就像便签
顺便说一句,即使赋值语句不直接导致变量的旧值被擦除和覆盖,你也不必担心计算机内存中充满“被丢弃”的值。如果一个值不再被任何变量引用,它就不再有用。Python将自动从内存中清除这些值,以便空间可以用于存放新值。这就像检查你的衣柜,抛出没有粘贴便签标记的东西。实际上,这个自动内存管理的过程确实被称为“垃圾收集”。
输入语句的目的是从程序的用户那里获取一些信息,并存储到变量中。一些编程语言有一个特殊的语句来做到这一点。在Python中,输入是用一个赋值语句结合一个内置函数input实现的。输入语句的确切形式,取决于你希望从用户那里获取的数据类型。对于文本输入,语句如下所示:
<variable> = input(<prompt>)
这里的<prompt>是一个字符串表达式,用于提示用户输入。提示几乎总是一个字符串字面量(即引号内的一些文本)。
当Python遇到对input的调用时,它在屏幕上打印提示。然后,Python暂停并等待用户键入一些文本,键入完成后按<Enter>键。用户输入的任何东西都会存储为字符串。请考虑以下简单的交互:
>>> name = input("Enter your name: ")
Enter your name: John Yaya
>>> name
'John Yaya'
执行input语句导致Python打印输出提示“Enter your name:”,然后解释器暂停,等待用户输入。在这个例子中,我键入John Yaya。结果,字符串“John Yaya”被记在变量name中。对name求值将返回我键入的字符串。
如果用户输入是一个数字,我们需要形式稍复杂一点的input语句:
<variable> = eval(input(<prompt>))
这里我添加了另一个内置的Python函数eval,它“包裹”了input函数。你可能会猜到,eval是“evaluate(求值)”的缩写。在这种形式中,用户键入的文本被求值为一个表达式,以产生存储到变量中的值。举例来说,字符串“32”就变成数字32。如果回头看看示例程序,到目前为止,你会看到几个例子,我们像这样从用户那里得到了数字。
x = eval(input("Please enter a number between 0 and 1: "))
celsius = eval(input("What is the Celsius temperature? "))
重要的是要记住,如果希望得到一个数字,而不是一些原始文本(字符串),需要对input进行eval。
如果你仔细阅读示例程序,可能会注意到所有这些提示结尾处的引号内的空格。我通常在提示的末尾放置一个空格,以便用户输入的内容不会紧接着提示开始。放上空格可以让交互更容易阅读和理解。
虽然我们的数字示例特别提示用户输入数字,但在这个例子中,用户键入的只是一个数字字面量,即一个简单的Python表达式。事实上,任何有效的表达式都是可接受的。请考虑下面与Python解释器的交互:
>>> ans = eval(input("Enter an expression: "))
Enter an expression: 3 + 4 * 5
>>> print(ans)
23
>>>
这里,提示输入表达式时,用户键入“3 + 4 * 5”。Python对此表达式求值(通过eval),并将值赋给变量ans。打印时,我们看到ans的值为23,与预期一样。在某种意义上,input-eval组合就像一个延迟的表达式。示例交互产生完全相同的结果,就像我们简单地写成ans = 3 + 4 * 5一样。不同的是,表达式由用户在语句执行时提供,而不是由程序员在编程时输入。
注意:eval函数功能非常强大,也有“潜在的危险”。如本例所示,当我们对用户输入求值时,本质上是允许用户输入一部分程序。Python将尽职尽责地对他们输入的任何内容求值。了解Python的人可以利用这种能力输入恶意指令。例如,用户可以键入记录计算机上的私人信息或删除文件的表达式。在计算机安全中,这被称为“代码注入”攻击,因为攻击者将恶意代码注入正在运行的程序中。
作为一名新程序员,编程给自己个人使用,计算机安全不是很大的问题。如果你坐在一台运行Python程序的计算机前面,你可能拥有对系统的完全访问权限,并且可以找到更简单的方法来删除所有文件。然而,如果一个程序的输入来自不受信任的来源,例如来自互联网上的用户,使用eval可能是灾难性的。幸运的是,你将在下一章看到一些更安全的替代方法。
有一个赋值语句的替代形式,允许我们同时计算几个值。它看起来像这样:
<var1>, <var2>, ..., <varn> = <expr1>, <expr2>, ..., <exprn>
这称为“同时赋值”。语义上,这告诉Python对右侧所有表达式求值,然后将这些值赋给左侧命名的相应变量。下面是一个例子:
sum, diff = x+y, x-y
这里,sum得到x和y的和,diff得到x和y的差。
这种形式的赋值初看很奇怪,但实际上非常有用。这里有一个例子:假设有两个变量x和y,你希望交换它们的值。也就是说,你希望将当前存储在x中的值存储在y中,将当前存储在y中的值存储在x中。首先,你可能认为这可以通过两个简单的赋值来完成:
x = y
y = x
这不行。我们可以一步一步地跟踪这些语句的执行,看看为什么。
假设x和y开始的值是2和4。让我们检查程序的逻辑,看看变量是如何变化的。以下序列用注释描述了在执行这两个语句时变量会发生什么:
# 变量 x y
# 初始值 2 4
x = y
# 现在是 4 4
y = x
# 最后是 4 4
看到第一个语句将y的值赋给x,从而修改了x的原始值吗?当我们在第二步将x的值赋给y时,最终得到了原始y值的两个副本。
完成交换的一种方法是引入一个附加变量,它暂时记住x的原始值。
temp = x
x = y
y = temp
让我们来看看这个序列是如何工作的。
# 变量 x y temp
# 初始值 2 4 暂时无值
temp = x
# 2 4 2
x = y
# 4 4 2
y = temp
# 4 2 2
从x和y的最终值可以看出,在这个例子中,交换成功。
这种三变量交换的方式在其他编程语言中很常见。在Python中,同时赋值语句提供了一种优雅的选择。下面是更简单的Python等价写法:
x, y = y, x
因为赋值是同时的,所以它避免了擦除一个原始值。
同时赋值也可以用单个input从用户那里获取多个数字。请考虑下面的程序,它求出考试平均分:
# avg2.py
# A simple program to average two exam scores
# Illustrates use of multiple input
def main():
print("This program computes the average of two exam scores.")
score1, score2 = eval(input("Enter two scores separated by a comma: "))
average = (score1 + score2) / 2
print("The average of the scores is:", average)
main()
该程序提示用逗号分隔两个分数。假设用户键入86,92。input语句的效果就像进行以下赋值:
score1, score2 = 86, 92
我们已经为每个变量获得了一个值。这个例子只用了两个值,但可以扩展到任意数量的输入。
当然,我们也可以通过单独的input语句获得用户的输入:
score1 = eval(input("Enter the first score: "))
score2 = eval(input("Enter the second score: "))
某种程度上,这可能更好,因为单独的提示对用户来说信息更准确。在这个例子中,决定采用哪种方法在很大程度上是品位问题。有时在单个input中获取多个值提供了更直观的用户接口,因此在你的工具包中,这是一项好技术。但要记住,多个值的技巧不适用于字符串(非求值)输入,如果用户键入逗号,它只是输入字符串中的一个字符。逗号仅在随后对字符串求值时,才成为分隔符。
你已经知道,程序员用循环连续多次执行一系列语句。最简单的循环称为“确定循环”。这是会执行一定次数的循环。也就是说,在程序中循环开始时,Python就知道循环(或“迭代”)的次数。例如,第1章中的chaos程序用了一个总是执行10次的循环:
for i in range(10):
x = 3.9 * x * (1 - x)
print(x)
这个特定的循环模式称为“计数循环”,它用Python的for语句构建。在详细分析这个例子之前,让我们来看看什么是for循环。
Python的for循环具有以下一般形式:
for <var> in <sequence>:
<body>
循环体可以是任意Python语句序列。循环体的范围通过它在循环头(for <var> in <sequence>:部分)下面的缩进来表示。
关键字for后面的变量称为“循环索引”。它依次取sequence中的每个值,并针对每个值都执行一次循环体中的语句。通常,sequence部分由值“列表”构成。列表是Python中一个非常重要的概念,你将在后续章节中了解更多。现在只要知道,可以在方括号中放置一系列表达式,从而创建一个简单的列表。下列交互示例有助于说明这一点:
>>> for i in [0, 1, 2, 3]:
print(i)
0
1
2
3
>>> for odd in [1, 3, 5, 7, 9]:
print(odd * odd)
1
9
25
49
81
你能看到这两个例子做了什么吗?依次使用列表中的每个值执行了循环体。列表的长度决定了循环执行的次数。在第一个例子中,列表包含4个值,即0至3,并且简单地打印了这些连续的i值。在第二个例子中,odd取前5个奇数的值,循环体打印了这些数字的平方。
现在,让我们回到这一节开始的例子(来自chaos.py)再看一下循环头:
for i in range(10):
将它与for循环的模板进行比较可以看出,最后一个部分range(10)必定是某种序列。事实上,range是一个内置的Python函数,用于“当场”生成一个数字序列。你可以认为range是一种数字序列的隐性描述。要明白range实际上做了什么,我们可以要求Python用另一个内置函数list,将range转换为一个简单的旧式列表:
>>> list(range(10)) # turns range(10) into an explicit list
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
你看到这里发生了什么吗?表达式range(10)产生数字0到9的序列。使用range(10)的循环等价于使用那些数字的列表的循环。
for i in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]:
一般来说,range(<expr>)将产生一个数字序列,从0开始,但不包括<expr>的值。如果你想一想,就会发现表达式的值确定了结果序列中的项数。在chaos.py中,我们甚至不关心循环索引变量使用了什么值(因为i没有在循环体中的任何位置引用)。我们只需要一个长度为10的序列,让循环体执行10次。
正如前面提到的,这种模式称为“计数循环”,它是使用确定循环的一种很常见的方式。如果你希望在程序中做一定次数的某些事,请用一个带有合适range的for循环。下面一个反复出现的Python编程习语,你需要记住:
for <variable> in range(<expr>):
表达式的值确定了循环执行的次数。索引变量的名称实际上并不重要,程序员经常使用i或j作为计数循环的循环索引变量。只要确保使用的标识符没有用于任何其他目的,否则你可能会不小心清除稍后需要的值。
循环的有趣和有用之处在于,它们改变程序“控制流”的方式。通常我们认为计算机是严格按顺序执行一系列指令。引入循环会导致Python退回去并重复执行一些语句。类似for循环的语句称为“控制结构”,因为它们控制程序其他部分的执行。
一些程序员发现,用图片的方式来思考控制结构是有帮助的,即所谓的“流程图”。流程图用一些框来表示程序的不同部分,并用框之间的箭头表示程序运行时的事件序列。图2.3用流程图描述了for循环的语义。
图2.3 for循环的流程图
如果你在理解for循环时遇到困难,可能会发现学习流程图很有用。流程图中的菱形框表示程序中的决定。当Python遇到循环头时,它检查序列中是否有项。如果答案为“是”,则循环索引变量被赋予序列中的下一项,然后执行循环体。一旦循环体完成,程序返回到循环头并检查序列中的下一个值。如果没有更多的项,循环就退出,程序移动到循环之后的语句。
我们用另一个编程过程的例子来结束本章。我们希望开发一个程序来确定投资的终值。我们将从对问题的分析开始。你知道存入银行账户的钱会赚取利息,这个利息随着时间的推移而累积。从现在起10年后,一个账户将有多少钱?显然,这取决于我们开始有多少钱(本金)以及账户赚多少利息。给定本金和利率,程序应该能够计算未来10年投资的终值。
我们继续制定程序的确切规格说明。记住,这是程序做什么的描述。输入应该是什么?我们需要用户输入初始投资金额,即本金。我们还需要说明账户赚多少利息。这取决于利率和计复利的频率。处理此问题的一种简单方法是让用户输入年度百分比率。无论实际利率和复利频率如何,年利率告诉我们一年内的投资收益。如果年利率为3%,那么100美元的投资将在一年的时间内增长到103美元。用户应如何表示年利率3%?有一些合理的选择。让我们假设用户提供一个小数,因此利率将输入为0.03。
这样就得到以下规格说明:
程序 终值
输入
principal 投资于美元的金额。
APR 以十进制数表示的年度百分比利率。
输出 投资10年后的终值。
关系 一年后的价值由principal(1 + apr)给出。该公式需要应用10次。
接下来为程序设计一个算法。我们将使用伪代码,这样就可以阐明我们的想法而又不必担心Python的所有规则。对于我们的规格说明,算法看起来很简单。
打印介绍
输入本金的金额(principal)
输入年度百分比利率(apr)
重复10次:
principal = principal *(1 + apr)
输出principal的值
如果你知道一些金融数学(或者只是一些基本代数)的知识,可能会意识到,在这个设计中并不一定要用循环。有一个公式可以利用乘幂一步算出终值。我在这里用了一个循环来展示另一个计数循环,另一个原因是这个版本适合进行一些修改,在本章末尾的编程练习中将讨论。无论如何,这个设计说明有时算法的计算方式可以让数学更容易。知道如何计算一年的利息,就让我们能计算未来任意年数的利息。
既然我们已经在伪代码中想明白了这个问题,现在该利用我们的Python新知识开发一个程序了。算法的每一行都转换为一条Python语句:
打印介绍(print语句,第2.4节)
print("This program calculates the future value")
print("of a 10-year investment.")
输入本金的金额(数值input,第2.5.2节)
principal = eval(input("Enter the initial principal: "))
输入年度百分比利率(数值input,第2.5.2节)
apr = eval(input("Enter the annual interest rate: "))
重复10次:(计数循环,第2.6节)
for i in range(10):
计算principal = principal * (1 + apr)(简单赋值,第2.5.1节)
principal = principal * (1 + apr)
输出principal的值(print语句,第2.4节)
print("The value in 10 years is:", principal)
该程序中的所有语句类型都已在本章中详细讨论过。如果有任何问题,请回头查看相关说明。特别要注意的是,计数循环模式用于应用10次利息公式。
就到这里了。下面是完成的程序:
# futval.py
# A program to compute the value of an investment
# carried 10 years into the future
def main():
print("This program calculates the future value")
print("of a 10-year investment.")
principal = eval(input("Enter the initial principal: "))
apr = eval(input("Enter the annual interest rate: "))
for i in range(10):
principal = principal * (1 + apr)
print("The value in 10 years is:", principal)
main()
注意,我添加了几个空行来分隔程序的输入、处理和输出部分。策略性地放置“空行”能让程序更具有可读性。
这就是我所举的例子,测试和调试是留给你的练习。
本章介绍了开发程序的过程,以及实现简单程序所需的许多Python细节。下面是一些要点的快速小结。
1.编写程序的最好方法是立即键入一些代码,然后调试它,直到它工作。
2.可以在不使用编程语言的情况下编写算法。
3.程序在写入和调试后不再需要修改。
4.Python标识符必须以字母或下划线开头。
5.关键词是好的变量名。
6.表达式由文字、变量和运算符构成。
7.在Python中,x = x + 1是一个合法的语句。
8.Python不允许使用单个语句输入多个值。
9.计数循环被设计为迭代特定次数。
10.在流程图中,菱形用于展示语句序列,矩形用于判断点。
1.以下 项不是软件开发过程中的一个步骤。
a.规格说明
b.测试/调试
c.决定费用
d.维护
2.将摄氏度转换为华氏度的正确公式是 。
a. F = 9/5(C) + 32
b.F = 5/9(C) − 32
c. F = B2 − 4AC
d.F = (212 – 32)/(100 – 0)
3.准确描述计算机程序将做什么来解决问题的过程称为 。
a.设计
b.实现
c.编程
d.规格说明
4.以下 项不是合法的标识符。
a.spam
b.spAm
c.2spam
d.spam4U
5.下列 不在表达式中使用。
a.变量
b.语句
c.操作符
d.字面量
6.生成或计算新数据值的代码片段被称为 。
a.标识符
b.表达式
c.生成子句
d.赋值语句
7.以下 项不是IPO模式的一部分。
a.输入
b.程序
c.处理
d.输出
8.模板for <variable> in range(<expr>)
描述了 。
a.一般for
循环
b.赋值语句
c.流程图
d.计数循环
9.以下 项是最准确的Python赋值模型。
a.粘贴便签
b.变量盒子
c.同时
d.塑料尺
10.在Python中,获取用户输入通过一个特殊的表达式来实现,称为 。
a.for
b.read
c.同时赋值
d.input
1.列出并用你自己的语言描述软件开发过程中的六个步骤。
2.写出chaos.py程序(第1.6节),并识别程序的各部分如下:
3.解释确定循环、for循环和计数循环几个概念之间的关系。
4.显示以下片段的输出:
a.
for i in range(5):
print(i * i)
b.
for d in [3,1,4,1,5]:
print(d, end=" ")
c.
for i in range(4):
print("Hello")
d.
for i in range(5):
print(i, 2**i)
5.先写出一个算法的伪代码而不是立即投入Python代码,为什么是一个好主意?
6.除end之外,Python的print
函数还支持其他关键字参数。其中一个关键字参数是sep
。你认为sep
参数是什么?(提示:sep
是分隔符的缩写。通过交互式执行或通过查阅Python文档来检验你的想法)。
7.如果执行下面的代码,你认为会发生什么?
print("start")
for i in range(0):
print("Hello")
print("end")
看看本章的for语句的流程图,帮助你弄明白。然后在程序中尝试这些代码,检验你的预测。
1.一个用户友好的程序应该打印一个介绍,告诉用户程序做什么。修改convert.py程序(第2.2节),打印介绍。
2.在许多使用Python的系统上,可以通过简单地点击(或双击)程序文件的图标来运行程序。如果你能够以这种方式运行convert.py程序,你可能会发现另一个可用性问题。 程序在新窗口中开始运行,但程序一完成,窗口就会消失,因此你无法读取结果。在程序结束时添加一个输入语句,让它暂停,给用户一个读取结果的机会。下面这样的代码应该有效:
input("Press the <Enter> key to quit.")
3.修改avg2.py
程序(第2.5.3节),找出三个考试成绩的平均值。
4.使用循环修改convert.py
程序(第2.2节),让它在退出前执行5次。每次通过循环,程序应该从用户获得另一个温度,并打印转换的值。
5.修改convert.py
程序(第2.2节),让它计算并打印一个摄氏温度和华氏度的对应表,从0℃到100℃,每隔10℃一个值。
6.修改futval.py
程序(第2.7节),让投资的年数也由用户输入。确保更改最后的消息,以反映正确的年数。
7.假设你有一个投资计划,每年投资一定的固定金额。修改futval.py
,计算你的投资的总累积值。该程序的输入将是每年投资的金额、利率和投资的年数。
8.作为APR的替代方案,账户所产生的利息通常通过名义利率和复利期数来描述。例如,如果利率为3%,利息按季度计算复利,则该账户实际上每3个月赚取0.75%的利息。请修改futval.py
程序,用此方法输入利率。程序应提示用户每年的利率(rate)和利息每年复利的次数(periods)。要计算10年的价值,程序将循环10 * periods次,并在每次迭代中累积rate/period的利息。
9.编写一个程序,将温度从华氏温度转换为摄氏温度。
10.编写一个程序,将以千米为单位的距离转换为英里。1千米约为0.62英里。
11.编写一个程序以执行你自己选择的单位转换。确保程序打印介绍,解释它的作用。
12.编写一个交互式Python计算器程序。程序应该允许用户键入数学表达式,然后打印表达式的值。加入循环,以便用户可以执行许多计算(例如,最多100个)。注意:要提前退出,用户可以通过键入一个错误的表达式,或简单地关闭计算器程序运行的窗口,让程序崩溃。在后续章节中,你将学习终止交互式程序的更好方法。