Python编程快速上手——让繁琐工作自动化

978-7-115-42269-9
作者: 【美】Al Sweigart(斯维加特)
译者: 王海鹏
编辑: 陈冀康
分类: Python

图书目录:

详情

本书快速教读者掌握Python这一对初学者友好的语言。书中包含了很多实用的示例,供读者学习和联系。通过本书,可以学会解决很多实际的任务和需求,包括在一个或多个文件中搜索文本模式、通过创建修改移动和重命名文件和文件夹来组织计算机、抓取数据和信息、更新Excel表格、自动发送邮件和文本消息、组织计算机执行周期性任务等等。

我们针对图书内容开发了在线编程练习题,并提供了在线编程环境,您可以边读书,边练习,在线编程,双效合一! 购买【异步社区VIP会员】或直接购买以下图书的【e读版电子书】后,即可在线畅读全书,并可通过章节末尾入口进入在线编程练习!了解更多精品图书在线编程实验课:https://www.epubit.com/topicsDetails?id=877f112b-febb-49f4-b203-ec6432852759  

图书摘要

版权信息

书名:Python编程快速上手——让繁琐工作自动化

ISBN:978-7-115-42269-9

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。

• 著    [美] Al Sweigart

  译    王海鹏

  责任编辑 陈冀康

• 人民邮电出版社出版发行  北京市丰台区成寿寺路11号

  邮编 100164  电子邮件 315@ptpress.com.cn

  网址 http://www.ptpress.com.cn

• 读者服务热线:(010)81055410

  反盗版热线:(010)81055315


Simplified Chinese-language edition copyright © 2016 by Posts and Telecom Press.

Copyright © 2015 by Al Sweigart.Title of English-language original: Automate The Boring Stuff with Python ISBN-13: 978-1-59327-599-0, published by No Starch Press.

All rights reserved.

本书中文简体字版由美国No Starch出版社授权人民邮电出版社出版。未经出版者书面许可,对本书任何部分不得以任何方式复制或抄袭。

版权所有,侵权必究。


如今,人们面临的大多数任务都可以通过编写计算机软件来完成。Python是一种解释型、面向对象、动态数据类型的高级程序设计语言。通过Python编程,我们能够解决现实生活中的很多任务。

本书是一本面向实践的Python编程实用指南。本书的目的,不仅是介绍Python语言的基础知识,而且还通过项目实践教会读者如何应用这些知识和技能。本书的第一部分介绍了基本的Python编程概念,第二部分介绍了一些不同的任务,通过编写Python程序,可以让计算机自动完成它们。第二部分的每一章都有一些项目程序,供读者学习。每章的末尾还提供了一些习题和深入的实践项目,帮助读者巩固所学的知识。附录部分提供了所有习题的解答。

本书适合任何想要通过Python学习编程的读者,尤其适合缺乏编程基础的初学者。通过阅读本书,读者将能利用最强大的编程语言和工具,并且将体会到Python编程的快乐。


Al Sweigart是一名软件开发者和技术图书作者,居住在旧金山。Python是他最喜欢的编程语言,他开发了几个开源模块。他的其他著作都在他的网站http://www. inventwithpython.com/上。


Ari Lacenski是Android应用程序和Python软件开发者。她住在旧金山,她写了一些关于Android编程的文章,放在http://gradlewhy.ghost.io/上,并与Women Who Code合作提供指导。她还是一个民谣吉他手。


没有很多人的帮助,我不可能写出这样一本书。我想感谢Bill Pollock,我的编辑Laurel Chun、Leslie Shen、Greg Poulos和Jennifer Griffith-Delgado,以及No Starch Press的其他工作人员,感谢他们非常宝贵的帮助。感谢我的技术评审Ari Lacenski,她提供了极好的建议、编辑和支持。

非常感谢Guido van Rossum,以及Python软件基金会的每个人,感谢他们了不起的工作。Python社区是我在业界看到的最佳社区。

最后,我要感谢我的家人和朋友,以及在Shotwell的伙伴,他们不介意我在写这本书时忙碌的生活。干杯!


这是机器代替人的时代,也是人控制机器的时代。这是程序员的时代,也是非程序员学编程的时代。这是算法的时代,也是编程语言的时代。翻译本书期间,深度学习的人工智能程序AlphaGo以4:1击败了李世石九段。

每一个不会编程的年轻人都应该认真考虑:是不是应该开始学习编程?

学习一门新的语言,总是让人感到畏缩。这让我想起大学时英语老师教的学习方法:听说领先,读写跟上。确实,学语言效果最好的方法就是“用”。本书就遵循了这样的宗旨。本书是面对编程初学者的书,假定读者没有任何编程知识。在简单介绍Python编程语言的基本知识后,就开始用一个接一个的例子,教我们如何用Python来完成一些日常工作,利用计算机这个强大的工具,节省工作时间,提高工作效率,避免手工操作容易带来的错误。

真正的程序员,用编程来解决自己和别人的问题。俄罗斯有一个程序员编写了一个程序,会给老婆发加班短信,会在宿醉不醒时给自己请假,会自动根据邮件恢复客户的数据库,还可以一键远程煮咖啡。加拿大一名零编程基础的农场主,在学习了一门编程课后,开发了一个程序,自动控制拖拉机,配合联合收割机收割谷物。

若是已经掌握了其他编程语言,想学习Python,本书也是不错的参考。每一种编程语言,都会提供一种独特的视角,让你对编程有新的认识。我非常喜欢Python没有花括号和分号,程序很“清爽”,符合奥卡姆剃刀原理:如无必要,勿增实体。本书并没有深入介绍面向对象和函数式编程范式,如果想了解Python这方面的内容,请参考其他书籍。

在本书的翻译过程,我自己也在项目中使用Python编程,从中得到许多启发。因此,郑重向大家推荐。翻译中的错误,请不吝指出。

王海鹏

2016年春于上海


“你在2个小时里完成的事,我们3个人要做两天。”21世纪早期,我的大学室友在一个电子产品零售商店工作。商店偶尔会收到一份电子表格,其中包含竞争对手的数千种产品的价格。由3个员工组成的团队,会将这个电子表格打印在一叠厚厚的纸上,然后3个人分一下。针对每个产品价格,他们会查看自己商店的价格,并记录竞争对手价格较低的所有产品。这通常会花几天的时间。

“如果你有打印件的原始文件,我会写一个程序来做这件事。”我的室友告诉他们,当时他看到他们坐在地板上,周围都是散落堆叠的纸张。

几个小时后,他写了一个简短的程序,从文件读取竞争对手的价格,在商店的数据库中找到该产品,并记录竞争对手是否更便宜。他当时还是编程新手,花了许多时间在一本编程书籍中查看文档。实际上程序只花了几秒钟运行。我的室友和他的同事们那天享受了超长的午餐。

这就是计算机编程的威力。计算机就像瑞士军刀,可以用来完成数不清的任务。许多人花上数小时点击鼠标和敲打键盘,执行重复的任务,却没有意识到,如果他们给机器正确的指令,机器就能在几秒钟内完成他们的工作。

软件是我们今天使用的许多工具的核心:几乎每个人都使用社交网络来进行交流,许多人的手机中都有连接因特网的计算机,大多数办公室工作都涉及操作计算机来完成工作。因此,对编程人才的需求暴涨。无数的图书、交互式网络教程和开发者新兵训练营,承诺将有雄心壮志的初学者变成软件工程师,获得6位数的薪水。

本书不是针对这些人的。它是针对所有其他的人。

就它本身来说,这本书不会让你变成一个职业软件开发者,就像几节吉他课程不会让你变成一名摇滚巨星。但如果你是办公室职员、管理者、学术研究者,或使用计算机来工作或娱乐的任何人,你将学到编程的基本知识,这样就能将下面这样一些简单的任务自动化:

对人来说,这些任务简单,但很花时间。它们通常很琐碎、很特殊,没有现成的软件可以完成。有一点编程知识,就可以让计算机为你完成这些任务。

本书没有设计成参考手册,它是初学者指南。编码风格有时候违反最佳实践(例如,有些程序使用全局变量),但这是一种折中,让代码更简单,以便学习。本书的目的是让人们编写用完即抛弃的代码,所以没有太多时间来关注风格和优雅。复杂的编程概念(如面向对象编程、列表推导和生成器),在本书中也没有介绍,因为它们增加了复杂性。编程老手可能会指出,本书中的代码可以修改得更有效率,但本书主要考虑的是用最少的工作量得到能工作的程序。

在电视剧和电影中,常常看到程序员在闪光的屏幕上迅速地输入密码般的一串1和0,但现代编程没有这么神秘。编程只是输入指令让计算机来执行。这些指令可能运算一些数字,修改文本,在文件中查找信息,或通过因特网与其他计算机通信。

所有程序都使用基本指令作为构件块。下面是一些常用的指令,用自然语言的形式来表示:

“做这个,然后做那个。”

“如果这个条件为真,执行这个动作,否则,执行那个动作。”

“按照指定次数执行这个动作。”

“一直做这个,直到条件为真。”

也可以组合这些构件块,实现更复杂的决定。例如,这里有一些编程指令,称为源代码,是用Python编程语言编写的一个简单程序。从头开始,Python软件执行每行代码(有些代码只有在特定条件为真时执行,否则Python会执行另外一些代码),直到到达底部。

❶ passwordFile = open('SecretPasswordFile.txt')
❷ secretPassword = passwordFile.read()
❸ print('Enter your password.')
typedPassword = input()
❹ if typedPassword == secretPassword:
❺     print('Access granted')
❻     if typedPassword == '12345':
❼         print('That password is one that an idiot puts on their luggage.')
else:
❽     print('Access denied')

你可能对编程一无所知,但读了上面的代码,也许就能够合理地猜测它做的事。首先,打开了文件SecretPasswordFile.txt❶,读取了其中的密码❷。然后,提示用户(通过键盘)输入一个密码❸。比较这两个密码❹,如果它们一样,程序就在屏幕上打印Access granted❺。接下来,程序检查密码是否为12345❻,提示说这可能并不是最好的密码❼。如果密码不一样,程序就在屏幕上打印Access denied❽。

Python指的是Python编程语言(包括语法规则,用于编写被认为是有效的Python代码),以及Python解释器软件,它读取源代码(用python语言编写),并执行其中的指令。Python解释器可以从http://python.org/免费下载,有针对Linux、OS X和Windows的版本。

Python的名字来自于英国超现实主义喜剧团体,而不是来自于蛇。Python程序员被亲切地称为Pythonistas。Monty Python和与蛇相关的引用常常出现在Python的指南和文档中。

我听到的关于学习编程的最常见的顾虑,就是人们认为这需要很多数学知识。其实,大多数编程需要的数学知识不超过基本算数。实际上,善于编程与善于解决数独问题没有太大差别。

要解决数独问题,数字1到9必须填入9×9的棋盘上每一行、每一列,以及每个3×3的内部方块。通过推导和起始数字的逻辑,你会找到一个答案。例如,在图 1的数独问题中,既然5出现在了左上角,它就不能出现在顶行、最左列,或左上角3×3方块中的其他位置。每次解决一行、一列或一个方块,将为剩下的部分提供更多的数字线索。

仅仅因为数独使用了数字,并不意味着必须精通数学才能求出答案。编程也是这样。就像解决数独问题一样,编程需要将一个问题分解为单个的、详细的步骤。类似地,在调试程序时(即寻找和修复错误),你会耐心地观察程序在做什么,找出缺陷的原因。像所有技能一样,编程越多,你就掌握得越好。

图1 一个新的数独问题(左边)及其答案(右边)。尽管使用了数字,数独并不需要太多数学知识

编程是一项创造性任务,有点类似于用乐高积木构建一个城堡。你从基本的想法开始,希望城堡看起来像怎样,并盘点可用的积木。然后开始构建。在你完成构建程序后,可以让代码变得更美观,就像对你的城堡那样。

编程与其他创造性活动的不同之处在于,在编程时,你需要的所有原材料都在计算机中,你不需要购买额外的画布、颜料、胶片、纱线、乐高积木或电子器件。在程序写好后,很容易将它在线共享给整个世界。尽管在编程时你会犯错,这项活动仍然很有乐趣。

本书的第一部分介绍了基本Python编程概念,第二部分介绍了一些不同的任务,你可以让计算机自动完成它们。第二部分的每一章都有一些项目程序,供你学习。下面简单介绍一下每章的内容。

第一部分:Python编程基础

“第 1 章:Python基础”介绍了表达式、Python指令的最基本类型,以及如何使用Python交互式环境来尝试运行代码。

“第 2 章:控制流”解释了如何让程序决定执行哪些指令,以便代码能够智能地响应不同的情况。

“第3章:函数”介绍了如何定义自己的函数,以便将代码组织成可管理的部分。

“第4章:列表”介绍了列表数据类型,解释了如何组织数据。

“第 5 章:字典和结构化数据”介绍了字典数据类型,展示了更强大的数据组织方法。

“第6章:字符串操作”介绍了处理文本数据(在Python中称为字符串)。

第二部分:自动化任务

“第7章:模式匹配与正则表达式”介绍了Python如何用正则表达式处理字符串,以及查找文本模式。

“第 8 章:读写文件”解释了程序如何读取文本文件的内容,并将信息保存到硬盘的文件中。

“第 9 章:组织文件”展示了Python如何用比手工操作快得多的速度,复制、移动、重命名和删除大量的文件,也解释了压缩和解压缩文件。

“第10章:调试”展示了如何使用Python的缺陷查找和缺陷修复工具。

“第 11 章:从Web抓取信息”展示了如何编程来自动下载网页,解析它们,获取信息。这称为从Web抓取信息。

“第 12 章:处理Excel电子表格”介绍了编程处理Excel电子表格,这样你就不必去阅读它们。如果你必须分析成百上千的文档,这是很有帮助的。

“第13章:处理PDF和Word文档”介绍了编程读取Word和PDF文档。

“第14章:处理CSV文件和JSON数据”解释了如何编程操作CSV和JSON文件。

“第15章:保持时间、计划任务和启动程序”解释了Python程序如何处理时间和日期,如何安排计算机在特定时间执行任务。这一章也展示了Python程序如何启动非Python程序。

“第16章:发送电子邮件和短信”解释了如何编程来发送电子邮件和短信。

“第17章:操作图像”解释了如何编程来操作JPG或PNG这样的图像。

“第18章:用GUI自动化控制键盘和鼠标”解释了如何编程控制鼠标和键盘,自动化鼠标点击和击键。

可以从http://python.org/downloads/免费下载针对Windows、OS X和Ubuntu的Python版本。如果你从该网站的下载页面下载了最新的版本,本书中的所有程序应该都能工作。

注意


请确保下载Python 3的版本(诸如3.4.0)。本书中的程序将运行在Python 3上,有一部分程序在Python 2上也许不能正常运行。

你需要在下载页面上找到针对64位或32位计算机以及特定操作系统的Python安装程序,所以先要弄清楚你需要哪个安装程序。如果你的计算机是2007年或以后购买的,很有可能是64位的系统。否则,可能是32位的系统,但下面是确认的方法:

在Windows上,下载Python安装程序(文件扩展名是.msi),并双击它。按照安装程序显示在屏幕上的指令来安装Python,步骤如下。

1.选择Install for All Users,然后点击Next。

2.通过点击Next安装到C:\Python34文件夹。

3.再次点击Next,跳过定制Python的部分。

在OS X上,下载适合你的OS X版本的.dmg文件,并双击它。按照安装程序显示在屏幕上的指令来安装Python,步骤如下。

1.当DMG包在一个新窗口中打开时,双击Python.mpkg文件。你可能必须输入管理员口令。

2.点击Continue,跳过欢迎部分,并点击Agree,接受许可证。

3.选择HD Macintosh(或者你的硬盘的名字),并点击Install。

如果使用的是Ubuntu,可以从终端窗口安装Python,步骤如下。

1.打开终端窗口。

2.输入sudo apt-get install python3。

3.输入sudo apt-get install idle3。

4.输入sudo apt-get install python3-pip。

Python解释器是运行Python程序的软件,而交互式开发环境(IDLE)是输入程序的地方,就像一个字处理软件。现在让我们启动IDLE。

无论你使用什么操作系统,初次出现的IDLE窗口应该基本上是空的,除了类似下面这样的文本:

Python 3.4.0 (v3.4.0:04f714765c13, Mar 16 2014, 19:25:23) [MSC v.1600 64
bit (AMD64)] on win32Type "copyright", "credits" or "license()" for more
information.
>>>

这个窗口称为交互式环境。这是让你向计算机输入指令的程序,很像OS X上的终端窗口,或Windows上的命令行提示符。Python的交互式环境让你输入指令,供Python解释器软件来执行。计算机读入你输入的指令,并立即执行它们。

例如,在交互式环境的>>>提示符后输入以下指令:

>>> print('Hello world!')

在输入该行并按下回车键后,交互式环境将显示以下内容作为响应:

>>> print('Hello world!')
Hello world!

独自解决编程问题可能比你想的要容易。如果你不相信,就让我们故意产生一个错误:在交互式环境中输入'42' + 3。现在你不需要知道这条指令是什么意思,但结果看起来应该像这样:

>>> '42' + 3
❶ Traceback (most recent call last):
  File "<pyshell#0>", line 1, in <module>
    '42' + 3
❷ TypeError: Can't convert 'int' object to str implicitly
>>>

这里出现了错误信息❷,因为Python不理解你的指令。错误信息的traceback部分❶显示了Python遇到困难的特定指令和行号。如果你不知道怎样处理特定的错误信息,就在线查找那条错误信息。在你喜欢的搜索引擎上输入“TypeError: Can't convert 'int' object to str implicitly”(包括引号),你就会看到许多的链接,解释这条错误信息的含义,以及什么原因导致这条错误,如图2所示。

你常常会发现,别人也遇到了同样的问题,而其他乐于助人的人已经回答了这个问题。没有人知道编程的所有方面,所以所有软件开发者的工作,都是每天在寻找技术问题的答案。

图2 错误信息的Google搜索结果可能非常有用

如果不能在线查找到答案,请尝试在Stack Overlow(http://stackoverflow.com/)或“learnprogramming”subreddit(http://reddit.com/r/learnprogramming/)这样的论坛上提问。但要记住,用聪明的方式提出编程问题,这有助于别人来帮助你。确保阅读这些网站的FAQ(常见问题),了解正确的提问方式。

在提出编程问题时,要记住以下几点。

也要遵守良好的在线礼节。例如,不要全用大写提问,或者对试图帮助你的人提出无理的要求。

对于大多数人,他们的计算机只是设备,而不是工具。但通过学习如何编程,你就能利用现代社会中最强大的工具,并且你会一直感到快乐。编程不是脑外科手术,业余人士是完全可以尝试或犯错的。

我喜欢帮助人们探索Python。我在自己的博客上编写编程指南(http:// inventwithpython.com/blog/),你可以发邮件向我提问(al@inventwithpython.com)。

本书将从零编程知识开始,但你的问题可能超出本书的范围。记住如何有效地提问,知道如何寻找答案,这对你的编程之旅是无价的工具。

让我们开始吧!



Python编程语言有许多语法结构、标准库函数和交互式开发环境功能。好在,你可以忽略大多数内容。你只需要学习部分内容,就能编写一些方便的小程序。

但在动手之前,你必须学习一些基本编程概念。就像魔法师培训,你可能认为这些概念既深奥又啰嗦,但有了一些知识和实践,你就能像魔法师一样指挥你的计算机,完成难以置信的事情。

本章有几个例子,我们鼓励你在交互式环境中输入它们。交互式环境让你每次执行一条Python指令,并立即显示结果。使用交互式环境对于了解基本Python指令的行为是很好的,所以你在阅读时要试一下。做过的事比仅仅读过的内容,更令人印象深刻。

启动IDLE就运行了交互式环境,这是和Python一起安装的。在Windows上,打开“开始”菜单,选择“All ProgramsPython 3.3”,然后选择“IDLE(Python GUI)”。在OS X上,选择“ApplicationsMacPython 3.3IDLE”。在Ubuntu上,打开新的终端窗口并输入idle3。

一个窗口会出现,包含>>>提示符,这就是交互式环境。在提示符后输入2 + 2,让Python做一些简单的算术。

>>> 2 + 2
4

IDLE窗口现在应该显示下面这样的文本:

Python 3.3.2 (v3.3.2:d047928ae3f6, May 16 2013, 00:06:53) [MSC v.1600 64 bit
(AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> 2 + 2
4
>>> 

在Python中,2 + 2称为“表达式”,它是语言中最基本的编程结构。表达式包含“值”(例如2)和“操作符”(例如+),并且总是可以求值(也就是归约)为单个值。这意味着在Python代码中,所有使用表达式的地方,也可以使用一个值。

在前面的例子中,2 + 2被求值为单个值4。没有操作符的单个值也被认为是一个表达式,尽管它求值的结果就是它自己,像下面这样:

>>> 2
2

错误没关系!


如果程序包含计算机不能理解的代码,就会崩溃,这将导致Python显示错误信息。错误信息并不会破坏你的计算机,所以不要害怕犯错误。“崩溃”只是意味着程序意外地停止执行。

如果你希望对一条错误信息了解更多,可以在网上查找这条信息的准确文本,找到关于这个错误的更多内容。也可以查看http://nostarch.com/automatestuff/,这里有常见的Python错误信息和含义的列表。

Python表达式中也可以使用大量其他操作符。例如,表 1-1 列出了Python的所有数学操作符。

表1-1 数学操作符,优先级从高到低

操作符

操作

例子

求值为

**

指数

2 ** 3

8

%

取模/取余数

22 % 8

6

//

整除/商数取整

22 // 8

2

/

除法

22 / 8

2.75

*

乘法

3 * 5

15

-

减法

5 - 2

3

+

加法

2 + 2

4

数学操作符的操作顺序(也称为“优先级”)与数学中类似。**操作符首先求值,接下来是­*、/、//和%操作符,从左到右。+和-操作符最后求值,也是从左到右。如果需要,可以用括号来改变通常的优先级。在交互式环境中输入下列表达式:

>>> 2 + 3 * 6
20
>>> (2 + 3) * 6
30
>>> 48565878 * 578453
28093077826734
>>> 2 ** 8
256
>>> 23 / 7
3.2857142857142856
>>> 23 // 7
3
>>> 23 % 7
2
>>> 2 + 2
4
>>> (5 - 1) * ((7 + 1) / (3 - 1))
16.0

在每个例子中,作为程序员,你必须输入表达式,但Python完成较难的工作,将它求值为单个值。Python将继续求值表达式的各个部分,直到它成为单个值,如图1-1所示。

图1-1 表达式求值将它归约为单个值

将操作符和值放在一起构成表达式的这些规则,是 Python 编程语言的基本部分,就像帮助我们沟通的语法规则一样。下面是例子:

This is a grammatically correct English sentence.

This grammatically is sentence not English correct a.

第二行很难解释,因为它不符合英语的规则。类似地,如果你输入错误的 Python指令,Python也不能理解,就会显示出错误信息,像下面这样:

>>> 5 +
  File "<stdin>", line 1
    5 +
      ^
SyntaxError: invalid syntax
>>> 42 + 5 + * 2
  File "<stdin>", line 1
    42 + 5 + * 2
              ^
SyntaxError: invalid syntax

你总是可以在交互式环境中输入一条指令,检查它是否能工作。不要担心会弄坏计算机:最坏的情况就是Python显示出错信息。专业的软件开发者在编写代码时,常常会遇到错误信息。

记住,表达式是值和操作符的组合,它们可以通过求值成为单个值。“数据类型”是一类值,每个值都只属于一种数据类型。表1-2列出了Python中最常见的数据类型。例如,值-2和30属于“整型”值。整型(或int)数据类型表明值是整数。带有小数点的数,如3.14,称为“浮点型”(或float)。请注意,尽管42是一个整型,但42.0是一个浮点型。

表1-2 常见数据类型

数据类型

例子

整型

-2, -1, 0, 1, 2, 3, 4, 5

浮点型

-1.25, -1.0, - -0.5, 0.0, 0.5, 1.0, 1.25

字符串

'a', 'aa', 'aaa', 'Hello!', '11 cats'

Python程序也可以有文本值,称为“字符串”,或strs(发音为“stirs”)。总是用单引号(')包围住字符串(例如'Hello'或'Goodbye cruel world!'),这样Python就知道字符串的开始和结束。甚至可以有没有字符的字符串,称为“空字符串”。第4章更详细地解释了字符串。

如果你看到错误信息SyntaxError: EOL while scanning string literal,可能是忘记了字符串末尾的单引号,如下面的例子所示:

>>> 'Hello world!
SyntaxError: EOL while scanning string literal

根据操作符之后的值的数据类型,操作符的含义可能会改变。例如,在操作两个整型或浮点型值时,+是相加操作符。但是,在用于两个字符串时,它将字符串连接起来,成为“字符串连接”操作符。在交互式环境中输入以下内容:

>>> 'Alice' + 'Bob'
'AliceBob'

该表达式求值为一个新字符串,包含了两个字符串的文本。但是,如果你对一个字符串和一个整型值使用加操作符,Python就不知道如何处理,它将显示一条错误信息。

>>> 'Alice' + 42
Traceback (most recent call last):
   File "<pyshell#26>", line 1, in <module>
     'Alice' + 42
TypeError: Can't convert 'int' object to str implicitly

错误信息Can't convert 'int' object to str implicitly表示Python认为,你试图将一个整数连接到字符串'Alice'。代码必须显式地将整数转换为字符串,因为Python不能自动完成转换。(1.6节“程序剖析”在讨论函数时,将解释数据类型转换。)

在用于两个整型或浮点型值时,­*­操作符表示乘法。但­*­操作符用于一个字符串值和一个整型值时,它变成了“字符串复制”操作符。在交互式环境中输入一个字符串乘一个数字,看看效果。

>>> 'Alice' * 5
'AliceAliceAliceAliceAlice'

该表达式求值为一个字符串,它将原来的字符串重复若干次,次数就是整型的值。字符串复制是一个有用的技巧,但不像字符串连接那样常用。

*操作符只能用于两个数字(作为乘法),或一个字符串和一个整型(作为字符串复制操作符)。否则,Python将显示错误信息。

>>> 'Alice' * 'Bob'
Traceback (most recent call last):
  File "<pyshell#32>", line 1, in <module>
    'Alice' * 'Bob'
TypeError: can't multiply sequence by non-int of type 'str'
>>> 'Alice' * 5.0
Traceback (most recent call last):
  File "<pyshell#33>", line 1, in <module>
    'Alice' * 5.0
TypeError: can't multiply sequence by non-int of type 'float'

Python不理解这些表达式是有道理的:你不能把两个单词相乘,也很难将一个任意字符串复制小数次。

“变量”就像计算机内存中的一个盒子,其中可以存放一个值。如果你的程序稍后将用到一个已求值的表达式的结果,就可以将它保存在一个变量中。

用“赋值语句”将值保存在变量中。赋值语句包含一个变量名、一个等号(称为赋值操作符),以及要存储的值。如果输入赋值语句spam = 42,那么名为spam的变量将保存一个整型值42。

可以将变量看成一个带标签的盒子,值放在其中,如图1-2所示。

图1-2 spam = 42 就像是告诉程序“变量spam现在有整数42放在里面”

例如,在交互式环境中输入以下内容:

❶ >>> spam = 40
 >>> spam
 40
 >>> eggs = 2
❷ >>> spam + eggs
 42
 >>> spam + eggs + spam
 82
❸ >>> spam = spam + 2
 >>> spam
 42

第一次存入一个值,变量就被“初始化”(或创建)❶。此后,可以在表达式中使用它,以及其他变量和值❷。如果变量被赋了一个新值,老值就被忘记了❸。这就是为什么在例子结束时,spam求值为42,而不是40。这称为“覆写”该变量。在交互式环境中输入以下代码,尝试覆写一个字符串:

>>> spam = 'Hello'
>>> spam
'Hello'
>>> spam = 'Goodbye'
>>> spam
'Goodbye'

就像图1-3中的盒子,这个例子中的spam变量保存了'Hello',直到你用'Goodbye'替代它。

图1-3 如果一个新值赋给变量,老值就被遗忘了

表1-3中有一些合法变量名的例子。你可以给变量取任何名字,只要它遵守以下3条规则:

1.只能是一个词。

2.只能包含字母、数字和下划线。

3.不能以数字开头。

表1-3 有效和无效的变量名

有效的变量名

无效的变量名

balance

current-balance(不允许中划线)

currentBalance

current balanc(不允许空格)

current_balance

4account(不允许数字开头)

_spam

42(不允许数字开头)

SPAM

total_$um(不允许$这样的特殊字符)

account4

'hello'(不允许'这样的特殊字符)

变量名是区分大小写的。这意味着,spam、 SPAM、Spam和sPaM是4个不同的变量。变量用小写字母开头是Python的惯例。

本书的变量名使用了驼峰形式,没有用下划线。也就是说,变量名用lookLikeThis,而不是looking_like_this。一些有经验的程序员可能会指出,官方的Python代码风格PEP 8,即应该使用下划线。我喜欢驼峰式,这没有错,并认为PEP 8本身“愚蠢的一致性是头脑狭隘人士的心魔”:

“一致地满足风格指南是重要的。但最重要的是,知道何时要不一致,因为有时候风格指南就是不适用。如果有怀疑,请相信自己的最佳判断。”

好的变量名描述了它包含的数据。设想你搬到一间新屋子,搬家纸箱上标的都是“东西”。你永远找不到任何东西!本书的例子和许多Python的文档,使用spam、eggs和bacon等变量名作为一般名称(受到Monty Python的“Spam”短剧的影响),但在你的程序中,具有描述性的名字有助于提高代码可读性。

虽然交互式环境对于一次运行一条 Python指令很好,但要编写完整的Python程序,就需要在文件编辑器中输入指令。“文件编辑器”类似于Notepad或TextMate这样的文本编辑器,它有一些针对输入源代码的特殊功能。要在IDLE中打开文件编辑器,请选择FileNew ▶Window。

出现的窗口中应该包含一个光标,等待你输入,但它与交互式环境不同。在交互式环境中,按下回车,就会执行Python指令。文件编辑器允许输入许多指令,保存为文件,并运行该程序。下面是区别这两者的方法:

现在是创建第一个程序的时候了!在文件编辑器窗口打开后,输入以下内容:

❶ # This program says hello and asks for my name.

❷ print('Hello world!')
 print('What is your name?') # ask for their name
❸ myName = input()
❹ print('It is good to meet you, ' + myName)
❺ print('The length of your name is:')
 print(len(myName))
❻ print('What is your age?') # ask for their age
 myAge = input()
 print('You will be ' + str(int(myAge) + 1) + ' in a year.') 

在输入完源代码后保存它,这样就不必在每次启动IDLE时重新输入。从文件编辑器窗口顶部的菜单,选择File▶Save As。在“Save As”窗口中,在输入框输入hello.py,然后点击“Save”。

在输入程序时,应该过一段时间就保存你的程序。这样,如果计算机崩溃,或者不小心退出了IDLE,也不会丢失代码。作为快捷键,可以在Windows和Linux上按Ctrl-S,在OS X上按⌘-S,来保存文件。

在保存文件后,让我们来运行程序。选择Run▶Run Module,或按下F5键。程序将在交互式环境窗口中运行,该窗口是首次启动IDLE时出现的。记住,必须在文件编辑器窗口中按F5,而不是在交互式环境窗口中。在程序要求输入时,输入你的名字。在交互式环境中,程序输出应该看起来像这样:

Python 3.3.2 (v3.3.2:d047928ae3f6, May 16 2013, 00:06:53) [MSC v.1600 64 bit
(AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> ================================ RESTART ================================
>>>
Hello world!
What is your name?
Al
It is good to meet you, Al
The length of your name is:
2
What is your age?
4
You will be 5 in a year.
>>> 

如果没有更多代码行要执行,Python程序就会“中止”。也就是说,它停止运行。(也可以说Python程序“退出”了。)

可以通过点击窗口上部的X,关闭文件编辑器。要重新加载一个保存了的程序,就在菜单中选择File▶Open。现在请这样做,在出现的窗口中选择hello.py,并点击“Open”按钮。前面保存的程序hello.py应该在文件编辑器窗口中打开。

新程序在文件编辑器中打开后,让我们快速看一看它用到的Python指令,逐一查看每行代码。

下面这行称为“注释”。

❶ # This program says hello and asks for my name. 

Python会忽略注释,你可以用它们来写程序注解,或提醒自己代码试图完成的事。这一行中,#标志之后的所有文本都是注释。

有时候,程序员在测试代码时,会在一行代码前面加上#,临时删除它。这称为“注释掉”代码。在你想搞清楚为什么程序不工作时,这样做可能有用。稍后,如果你准备还原这一行代码,可以去掉#。

Python也会忽略注释之后的空行。在程序中,想加入空行时就可以加入。这会让你的代码更容易阅读,就像书中的段落一样。

print()函数将括号内的字符串显示在屏幕上。

❷ print('Hello world!')
 print('What is your name?') # ask for their name

代码行print('Hello world!')表示“打印出字符串'Hello world!'的文本”。Python执行到这行时,你告诉Python调用print()函数,并将字符串“传递”给函数。传递给函数的值称为“参数”。请注意,引号没有打印在屏幕上。它们只是表示字符串的起止,不是字符串的一部分。

也可以用这个函数在屏幕上打印出空行,只要调用print()就可以了,括号内没有任何东西。

在写函数名时,末尾的左右括号表明它是一个函数的名字。这就是为什么在本书中你会看到print(),而不是print。第2章更详细地探讨了函数。

函数等待用户在键盘上输入一些文本,并按下回车键。

❸ myName = input()

这个函数求值为一个字符串,即用户输入的文本。前面的代码行将这个字符串赋给变量myName。

你可以认为input()函数调用是一个表达式,它求值为用户输入的任何字符串。如果用户输入'Al',那么该表达式就求值为myName = 'Al'。

接下来的print()调用,在括号间包含表达式'It is good to meet you, ' + myName。

❹ print('It is good to meet you, ' + myName) 

要记住,表达式总是可以求值为一个值。如果'Al'是上一行代码保存在myName中的值,那么这个表达式就求值为'It is good to meet you, Al'。这个字符串传给print(),它将输出到屏幕上。

你可以向len()函数传递一个字符串(或包含字符串的变量),然后该函数求值为一个整型值,即字符串中字符的个数。

❺ print('The length of your name is:')
 print(len(myName)) 

在交互式环境中输入以下内容试一试:

>>> len('hello')
5
>>> len('My very energetic monster just scarfed nachos.')
46
>>> len('')
0

就像这些例子,len(myName)求值为一个整数。然后它被传递给print(),在屏幕上显示。请注意,print()允许传入一个整型值或字符串。但如果在交互式环境中输入以下内容,就会报错:

 >>> print('I am ' + 29 + ' years old.')
Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
    print('I am ' + 29 + ' years old.')
TypeError: Can't convert 'int' object to str implicitly

导致错误的原因不是print()函数,而是你试图传递给print()的表达式。如果在交互式环境中单独输入这个表达式,也会得到同样的错误。

>>> 'I am ' + 29 + ' years old.'
Traceback (most recent call last):
  File "<pyshell#7>", line 1, in <module>
   'I am ' + 29 + ' years old.'
TypeError: Can't convert 'int' object to str implicitly

报错是因为,只能用+操作符加两个整数,或连接两个字符串。不能让一个整数和一个字符串相加,因为这不符合Python的语法。可以使用字符串版本的整数,修复这个错误。这在下一节中解释。


虽然数字的字符串值被认为与整型值和浮点型值完全不同,但整型值可以与浮点值相等。

Python进行这种区分,因为字符串是文本,而整型值和浮点型都是数字。

如果想要连接一个整数(如29)和一个字符串,再传递给print(),就需要获得值'29'。它是29的字符串形式。str()函数可以传入一个整型值,并求值为它的字符串形式,像下面这样:

>>> str(29)
'29'
>>> print('I am ' + str(29) + ' years old.')
I am 29 years old. 
>>> 42 == '42'
False
>>> 42 == 42.0
True
>>> 42.0 == 0042.000
True

因为str(29)求值为’29’,所以表达式'I am ' + str(29) + ' years old.'求值为'I am ' + '29' + ' years old.',它又求值为'I am 29 years old.'。这就是传递给print()函数的值。

str()、int()和float()函数将分别求值为传入值的字符串、整数和浮点数形式。请尝试用这些函数在交互式环境中转换一些值,看看会发生什么。

>>> str(0)
'0'
>>> str(-3.14)
'-3.14'
>>> int('42')
42
>>> int('-99')
-99
>>> int(1.25)
1
>>> int(1.99)
1
>>> float('3.14')
3.14
>>> float(10)
10.0

前面的例子调用了str()、int()和float()函数,向它们传入其他数据类型的值,得到了字符串、整型或浮点型的值。

如果想要将一个整数或浮点数与一个字符串连接,str()函数就很方便。如果你有一些字符串值,希望将它们用于数学运算,int()函数也很有用。例如,input()函数总是返回一个字符串,即便用户输入的是一个数字。在交互式环境中输入spam = input(),在它等待文本时输入101。

>>> spam = input()
101
>>> spam
'101'

保存在spam中的值不是整数101,而是字符串'101'。如果想要用spam中的值进行数学运算,那就用int()函数取得spam的整数形式,然后将这个新值存在spam中。

>>> spam = int(spam)
>>> spam
101

现在你应该能将spam变量作为整数,而不是字符串使用。

>>> spam * 10 / 5
202.0

请注意,如果你将一个不能求值为整数的值传递给int(),Python将显示出错信息。

>>> int('99.99')
Traceback (most recent call last):
  File "<pyshell#18>", line 1, in <module>
    int('99.99')
ValueError: invalid literal for int() with base 10: '99.99'
>>> int('twelve')
Traceback (most recent call last):
  File "<pyshell#19>", line 1, in <module>
    int('twelve')
ValueError: invalid literal for int() with base 10: 'twelve'

如果需要对浮点数进行取整运算,也可以用int()函数。

>>> int(7.7)
7
>>> int(7.7) + 1
8

在你的程序中,最后3行使用了函数int()和str(),取得适当数据类型的值。

❻ print('What is your age?') # ask for their age
 myAge = input()
 print('You will be ' + str(int(myAge) + 1) + ' in a year.') 

myAge变量包含了input()函数返回的值。因为input()函数总是返回一个字符串(即使用户输入的是数字),所以你可以使用int(myAge)返回字符串的整型值。这个整型值随后在表达式int(myAge) + 1中与1相加。

相加的结果传递给str()函数:str(int(myAge) + 1)。然后,返回的字符串与字符串'You will be '和' in a year.'连接,求值为一个更长的字符串。这个更长的字符串最终传递给print(),在屏幕上显示。

假定用户输入字符串'4',保存在myAge中。字符串'4'被转换为一个整型,所以你可以对它加1。结果是5。str()函数将这个结果转化为字符串,这样你就可以将它与第二个字符串'in a year.'连接,创建最终的消息。这些求值步骤如图1-4所示。

文本和数字相等判断

图1-4 如果4保存在myAge中,求值的步骤

你可以用一个计算器来计算表达式,或在文本处理器中输入字符串连接。甚至可以通过复制粘贴文本,很容易地实现字符串复制。但是表达式以及组成它们的值(操作符、变量和函数调用),才是构成程序的基本构建块。一旦你知道如何处理这些元素,就能够用Python操作大量的数据。

最好是记住本章中介绍的不同类型的操作符(+、-、­*­、/、//、%和­**­是数学操作符,+和­*­是字符串操作符),以及3种数据类型(整型、浮点型和字符串)。

我们还介绍了几个不同的函数。print()和input()函数处理简单的文本输出(到屏幕)和输入(通过键盘)。len()函数接受一个字符串,并求值为该字符串中字符的数目。

在下一章中,你将学习如何告诉Python根据它拥有的值,明智地决定什么代码要运行,什么代码要跳过,什么代码要重复。这称为“控制流”,它让你编写程序来做出明智的决定。

1.下面哪些是操作符,哪些是值?

*
'hello'
-88.8
-
/
+
5

2.下面哪个是变量,哪个是字符串?

spam
'spam'

3.说出3种数据类型。

4.表达式由什么构成?所有表达式都做什么事?

5.本章介绍了赋值语句,如spam = 10。表达式和语句有什么区别?

6.下列语句运行后,变量bacon的值是什么?

bacon = 20
bacon + 1

7.下面两个表达式求值的结果是什么?

'spam' + 'spamspam'
'spam' * 3

8.为什么eggs是有效的变量名,而100是无效的?

9.哪3个函数能分别取得一个值的整型、浮点型或字符串版本?

10.为什么这个表达式会导致错误?如何修复?

'I have eaten ' + 99 + ' burritos.'

附加题:在线查找len()函数的Python文档。它在一个标题为“Built-in Functions”的网页上。扫一眼Python的其他函数的列表,查看round()函数的功能,在交互式环境中使用它。

你已经知道了单条指令的基本知识。程序就是一系列指令。但编程真正的力量不仅在于运行(或“执行”)一条接一条的指令,就像周末的任务清单那样。根据表达式求值的结果,程序可以决定跳过指令,重复指令,或从几条指令中选择一条运行。实际上,你几乎永远不希望程序从第一行代码开始,简单地执行每行代码,直到最后一行。“控制流语句”可以决定在什么条件下执行哪些Python语句。

这些控制流语句直接对应于流程图中的符号,所以在本章中,我将提供示例代码的流程图。图2-1展示了一张流程图,内容是如果下雨怎么办。按照箭头构成的路径,从开始到结束。

在流程图中,通常有不止一种方法从开始走到结束。计算机程序中的代码行也是这样。流程图用菱形表示这些分支节点,其他步骤用矩形表示。开始和结束步骤用带圆角的矩形表示。

但在学习流程控制语句之前,首先要学习如何表示这些yes和no选项。同时你也需要理解,如何将这些分支节点写成Python代码。要做到这一点,让我们先看看布尔值、比较操作符和布尔操作符。

图2-1 一张流程图,告诉你如果下雨要做什么

虽然整型、浮点型和字符串数据类型有无数种可能的值,但“布尔”数据类型只有两种值:True和False。Boolean(布尔)的首字母大写,因为这个数据类型是根据数学家George Boole命名的。在作为Python代码输入时,布尔值True和False不像字符串,两边没有引号,它们总是以大写字母T或F开头,后面的字母小写。在交互式环境中输入下面内容,其中有些指令是故意弄错的,它们将导致出错信息。

❶ >>> spam = True
 >>> spam
 True
❷>>> true
 Traceback (most recent call last):
   File "<pyshell#2>", line 1, in <module>
     true
 NameError: name 'true' is not defined
❸ >>> True = 2 + 2
 SyntaxError: assignment to keyword

像其他值一样,布尔值也用在表达式中,并且可以保存在变量中❶。如果大小写不正确❷,或者试图使用True和False作为变量名❸,Python就会给出错误信息。

“比较操作符”比较两个值,求值为一个布尔值。表2-1列出了比较操作符。

表2-1 比较操作符

操作符

含义

==

等于

!=

不等于

<

小于

>

大于

<=

小于等于

>=

大于等于

这些操作符根据给它们提供的值,求值为True或False。现在让我们尝试一些操作符,从==和!=开始。

>>>  42 == 42
True
>>>  42 == 99
False
>>>  2 != 3
True
>>>  2 != 2
False

如果两边的值一样,==(等于)求值为True。如果两边的值不同,!=(不等于)求值为False。==和!=操作符实际上可以用于所有数据类型的值。

 >>> 'hello' == 'hello'
 True
 >>> 'hello' == 'Hello'
 False
 >>> 'dog' != 'cat'
 True
 >>> True == True
 True
 >>> True != False
 True
 >>> 42 == 42.0
 True
❶ >>> 42 == '42'
 False
 

请注意,整型或浮点型的值永远不会与字符串相等。表达式42 == '42'❶求值为False是因为,Python认为整数42与字符串'42'不同。

另一方面,<、>、<=和>=操作符仅用于整型和浮点型值。

 >>> 42 < 100
 True
 >>> 42 > 100
 False
 >>> 42 < 42
 False
 >>> eggCount = 42
❷>>> eggCount <= 42
 True
 >>> myAge = 29
❸ >>> myAge >= 10
 True 

操作符的区别


你可能已经注意到,==操作符(等于)有两个等号,而=操作符(赋值)只有一个等号。这两个操作符很容易混淆。只要记住:

  • ==操作符(等于)问两个值是否彼此相同。

  • =操作符(赋值)将右边的值放到左边的变量中。

为了记住谁是谁,请注意==操作符(等于)包含两个字符,就像!=操作符(不等于)包含两个字符一样。

你会经常用比较操作符比较一个变量和另外某个值。就像在例子eggCount <= 42❶和myAge >= 10❷中一样(毕竟,除了在代码中输入'dog' != 'cat'以外,你本来也可以直接输入True)。稍后,在学习控制流语句时,你会看到更多的例子。

3个布尔操作符(and、or和not)用于比较布尔值。像比较操作符一样,它们将这些表达式求值为一个布尔值。让我们仔细看看这些操作符,从and操作符开始。

and和or操作符总是接受两个布尔值(或表达式),所以它们被认为是“二元”操作符。如果两个布尔值都为True,and操作符就将表达式求值为True,否则求值为False。在交互式环境中输入某个使用and的表达式,看看效果。

>>> True and True
True
>>> True and False
False

“真值表”显示了布尔操作符的所有可能结果。表2-2是操作符and的真值表。

表2-2 and操作符的真值表

表达式

求值为

True and True

True

True and False

False

False and True

False

False and False

False

另一方面,只要有一个布尔值为真,or操作符就将表达式求值为True。如果都是False,所求值为False。

>>> False or True
True
>>> False or False
False

可以在or操作符的真值表中看到每一种可能的结果,如表2-3所示。

表2-3 or操作符的真值表

表达式

求值为

True or True

True

True or False

True

False or True

True

False or False

False

和and和or不同,not操作符只作用于一个布尔值(或表达式)。not操作符求值为相反的布尔值。

 >>> not  True
 False
❶ >>> not not not not True
 True

就像在说话和写作中使用双重否定,你可以嵌套not操作符❶,虽然在真正的程序中并不经常这样做。表2-4展示了not的真值表。

表2-4 not操作符的真值表

表达式

求值为

not True

False

not False

True

既然比较操作符求值为布尔值,就可以和布尔操作符一起,在表达式中使用。

回忆一下,and、or和not操作符称为布尔操作符是因为,它们总是操作于布尔值。虽然像4 < 5这样的表达式不是布尔值,但可以求值为布尔值。在交互式环境中,尝试输入一些使用比较操作符的布尔表达式。

>>> (4 < 5) and (5 < 6)
True
>>> (4 < 5) and (9 < 6)
False
>>> (1 == 2) or (2 == 2)
True

计算机将先求值左边的表达式,然后再求值右边的表达式。知道两个布尔值后,它又将整个表达式再求值为一个布尔值。你可以认为计算机求值(4 < 5)和(5 < 6)的过程,如图2-2所示。

图2-2 (4 < 5)和 (5 < 6) 求值为True的过程

也可以在一个表达式中使用多个布尔操作符,与比较操作符一起使用。

>>> 2 + 2 == 4 and not 2 + 2 == 5 and 2 * 2 == 2 + 2
True

和算术操作符一样,布尔操作符也有操作顺序。在所有算术和比较操作符求值后,Python先求值not操作符,然后是and操作符,然后是or操作符。

控制流语句的开始部分通常是“条件”,接下来是一个代码块,称为“子句”。在开始学习具体的Python控制流语句之前,我将介绍条件和代码块。

你前面看到的布尔表达式可以看成是条件,它和表达式是一回事。“条件”只是在控制流语句的上下文中更具体的名称。条件总是求值为一个布尔值,True或False。控制流语句根据条件是True还是False,来决定做什么。几乎所有的控制流语句都使用条件。

一些代码行可以作为一组,放在“代码块”中。可以根据代码行的缩进,知道代码块的开始和结束。代码块有3条规则。

1.缩进增加时,代码块开始。

2.代码块可以包含其他代码块。

3.缩进减少为零,或减少为外面包围代码块的缩进,代码块就结束了。

看一些有缩进的代码,更容易理解代码块。所以让我们在一小段游戏程序中,寻找代码块,如下所示:

 if name == 'Mary':
❶     print('Hello Mary')
 if password == 'swordfish':
❷    print('Access granted.')
 else:
❸     print('Wrong password.')

第一个代码块❶开始于代码行print('Hello Mary'),并且包含后面所有的行。在这个代码块中有另一个代码块❷,它只有一行代码:print('Access Granted.')。第三个代码块❸也只有一行:print('Wrong password.')。

在第1章的hello.py程序中,Python开始执行程序顶部的指令,然后一条接一条往下执行。“程序执行”(或简称“执行”)这一术语是指当前被执行的指令。如果将源代码打印在纸上,在它执行时用手指指着每一行代码,你可以认为手指就是程序执行。

但是,并非所有的程序都是从上至下简单地执行。如果用手指追踪一个带有控制流语句的程序,可能会发现手指会根据条件跳过源代码,有可能跳过整个子句。

现在,让我们来看最重要的控制流部分:语句本身。语句代表了在图2-1的流程图中看到的菱形,它们是程序将做出的实际决定。

最常见的控制流语句是if语句。if语句的子句(也就是紧跟if语句的语句块),将在语句的条件为True时执行。如果条件为False,子句将跳过。

在英文中,if语句念起来可能是:“如果条件为真,执行子句中的代码。”在Python中,if语句包含以下部分:

例如,假定有一些代码,检查某人的名字是否为Alice(假设此前曾为name赋值)。

if name == 'Alice':
    print('Hi, Alice.')

所有控制流语句都以冒号结尾,后面跟着一个新的代码块(子句)。语句的if子句是代码块,包含print('Hi, Alice.')。图2-3展示了这段代码的流程图。

图2-3 if语句的流程图

if子句后面有时候也可以跟着else语句。只有if语句的条件为False时,else子句才会执行。在英语中,else语句读起来可能是:“如果条件为真,执行这段代码。否则,执行那段代码”。else语句不包含条件,在代码中,else语句中包含下面部分:

回到Alice的例子,我们来看看使用else语句的一些代码,在名字不是Alice时,提供不一样的问候。

if name == 'Alice':
    print('Hi, Alice.')
else:
    print('Hello, stranger.')

图2-4展示了这段代码的流程图。

图2-4 else语句的流程图

虽然只有if或else子句会被执行,但有时候可能你希望,“许多”可能的子句中有一个被执行。elif语句是“否则如果”,总是跟在if或另一条elif语句后面。它提供了另一个条件,仅在前面的条件为False时才检查该条件。在代码中,elif语句总是包含以下部分:

让我们在名字检查程序中添加elif,看看这个语句的效果。

if name == 'Alice':
    print('Hi, Alice.')
elif age < 12:
    print('You are not Alice, kiddo.')

这一次,检查此人的年龄。如果比 12 岁小,就告诉他一些不同的东西。可以在图2-5中看到这段代码的流程图。

如果age < 12为True并且name == 'Alice'为False,elif子句就会执行。但是,如果两个条件都为False,那么两个子句都会跳过。“不能”保证至少有一个子句会被执行。如果有一系列的elif语句,仅有一条或零条子句会被执行。一旦一个语句的条件为True,剩下的elif子句会自动跳过。例如,打开一个新的文件编辑器窗口,输入以下代码,保存为vampire.py。

if name == 'Alice':
    print('Hi, Alice.')
elif age < 12:
    print('You are not Alice, kiddo.')
elif age > 2000:
    print('Unlike you, Alice is not an undead, immortal vampire.')
elif age > 100:
    print('You are not Alice, grannie.')

图2-5 elif语句的流程图

这里,我添加了另外两条elif语句,让名字检查程序根据age的不同答案而发出问候。图2-6展示了这段代码的流程图。

但是,elif语句的次序确实重要。让我们重新排序,引入一个缺陷。回忆一下,一旦找到一个True条件,剩余的子句就会自动跳过。所以如果交换vampire.py中的一些子句,就会遇到问题。像下面这样改变代码,将它保存为vampire2.py。

 if name == 'Alice':
     print('Hi, Alice.')
 elif age < 12:
     print('You are not Alice, kiddo.')
❶ elif age > 100:
     print('You are not Alice, grannie.')
 elif age > 2000:
     print('Unlike you, Alice is not an undead, immortal vampire.')

图2-6 vampire.py程序中多重elif语句的流程图

假设在这段代码执行之前,age变量的值是3000。你可能预计代码会打印出字符串'Unlike you, Alice is not an undead, immortal vampire.'。但是,因为age > 100条件为真(毕竟3000大于100)❶,字符串'You are not Alice, grannie.'被打印出来,剩下的语句自动跳过。别忘了,最多只有一个子句会执行,对于elif语句,次序是很重要的。

图2-7展示了前面代码的流程图。请注意,菱形age > 100和age > 2000交换了位置。

图2-7 vampire2.py程序的流程图。打叉的路径在逻辑上永远不会发生,
因为如果age大于2000,它就已经大于100了

你可以选择在最后的elif语句后面加上else语句。在这种情况下,保证至少一个子句(且只有一个)会执行。如果每个if和elif语句中的条件都为False,就执行else子句。例如,让我们使用if、elif和else子句重新编写Alicee程序。

if name == 'Alice':
    print('Hi, Alice.')
elif age < 12:
    print('You are not Alice, kiddo.')
else:
    print('You are neither Alice nor a little kid.')

图2-8展示了这段新代码的流程图,我们将它保存为littleKid.py。

图2-8 前面littleKid.py程序的流程图

在英语中,这类控制流结构会使得:“如果第一个条件为真,做这个。否则,如果第二个条件为真,做那个。否则,做另外的事。”如果你同时使用这3个语句,要记住这些次序规则,避免图2-7中那样的缺陷。首先,总是只有一个if语句。所有需要的elif语句都应该跟在if语句之后。其次,如果希望确保至少一条子句被执行,在最后加上else语句。

利用while语句,可以让一个代码块一遍又一遍的执行。只要while语句的条件为True,while子句中的代码就会执行。在代码中,while语句总是包含下面几部分:

可以看到,while语句看起来和if语句类似。不同之处是它们的行为。if子句结束时,程序继续执行if语句之后的语句。但在while子句结束时,程序执行跳回到while语句开始处。while子句常被称为“while循环”,或就是“循环”。

让我们来看一个if语句和一个while循环。它们使用同样的条件,并基于该条件做出同样的动作。下面是if语句的代码:

spam = 0
if spam < 5:
    print('Hello, world.')
    spam = spam + 1

下面是while语句的代码:

spam = 0
while spam < 5:
    print('Hello, world.')
    spam = spam + 1

这些语句类似,if和while都检查spam的值,如果它小于5,就打印一条消息。但如果运行这两段代码,它们各自的表现非常不同。对于if语句,输出就是"Hello, world."。但对于while语句,输出是"Hello, world."重复了5次!看一看这两段代码的流程图,图2-9和2-10,找一找原因。

图2-9 if语句代码的流程图

图2-10 while语句代码的流程图

带有if语句的代码检查条件,如果条件为True,就打印一次"Hello, world."。带有while循环的代码则不同,会打印5次。打印5次后停下来是因为,在每次循环迭代末尾,spam中的整数都增加1。这意味着循环将执行5次,然后spam < 5变为False。

在while循环中,条件总是在每次“迭代”开始时检查(也就是每次循环执行时)。如果条件为True,子句就会执行,然后,再次检查条件。当条件第一次为False时,while子句就跳过。

这里有一个小例子,它不停地要求你输入“your name”(就是这个字符串,而不是你的名字)。选择FileNew Window,打开一个新的文件编辑器窗口,输入以下代码,将文件保存为yourName.py:

❶ name = ''
❷while name != 'your name':
     print('Please type your name.')
❸     name = input()
❹ print('Thank you!')

首先,程序将变量name❶设置为一个空字符串。这样,条件name != 'your name'就会求值为True,程序就会进入while循环的子句❷。

这个子句内的代码要求用户输入他们的名字,然后赋给name变量❸。因为这是语句块的最后一行,所以执行就回到while循环的开始,重新对条件求值。如果name中的值“不等于”字符串'your name',那么条件就为True,执行将再次进入while子句。

但如果用户输入your name,while循环的条件就变成'your name' != 'your name',它求值为False。条件现在是False,程序就不会再次进入while循环子句,而是跳过它,继续执行程序后面的部分❹。图2-11展示了yourName.py程序的流程图。

图2-11 yourName.py程序的流程图

现在,让我们来看看yourName.py程序的效果。按F5键运行它,输几次your name之外的东西,然后再提供程序想要的输入。

Please type your name.
Al
Please type your name.
Albert
Please type your name.
%#@#%*(^&!!!
Please type your name.
your name
Thank you!

如果永不输入your name,那么循环的条件就永远为False,程序将永远问下去。这里,input()调用让用户输入正确的字符串,以便让程序继续。在其他程序,条件可能永远没有实际变化,这可能会出问题。让我们来看看如何跳出循环。

有一个捷径,让执行提前跳出while循环子句。如果执行遇到break语句,就会马上退出while循环子句。在代码中,break语句仅包含break关键字。

非常简单,对吗?这里有一个程序,和前面的程序做一样的事情,但使用了break语句来跳出循环。输入以下代码,将文件保存为yourName2.py:

❶ while True:
     print('Please type your name.')
❷    name = input()
❸     if name == 'your name':
❹         break
❺ print('Thank you!')

第一行❶创建了一个“无限循环”,它是一个条件总是为True的while循环。(表达式True总是求值为True。)程序执行将总是进入循环,只有遇到break语句执行时才会退出(“永远不”退出的无限循环是一个常见的编程缺陷)。

像以前一样,程序要求用户输入your name❷。但是现在,虽然执行仍然在while循环内,但有一个if语句会被执行❸,检查name是否等于your name。如果条件为True,break语句就会运行❹,执行就会跳出循环,转到print('Thank you!') ❺。否则,包含break语句的if语句子句就会跳过,让执行到达while循环的末尾。此时,程序执行跳回到while语句的开始❶,重新检查条件。因为条件是True,所以执行进入循环,再次要求用户输入your name。这个程序的流程图参见图2-12。

运行yourName2.py,输入你为yourName.py程序输入的同样文本。重写的程序应该和原来的程序反应相同。

图2-12 带有无限循环的程序的流程图。注意打叉路径在逻辑上
永远不会发生,因为循环条件总是为True


如果你运行一个有缺陷的程序,导致陷在一个无限循环中,那么请按Ctrl-C。这将向程序发送KeyboardInterrupt错误,导致它立即停止。试一下,在文件编辑器中创建一个简单的无限循环,将它保存为infiniteloop.py。

如果运行这个程序,它将永远在屏幕上打印Hello world!因为while语句的条件总是True。在IDLE的交互式环境窗口中,只有两种办法停止这个程序:按下Ctrl-C或从菜单中选择ShellRestart Shell。如果你希望马上停止程序,即使它不是陷在一个无限循环中,Ctrl-C也是很方便的。

像break语句一样,continue语句用于循环内部。如果程序执行遇到continue语句,就会马上跳回到循环开始处,重新对循环条件求值(这也是执行到达循环末尾时发生的事情)。

while True:
   print('Hello world!')

让我们用continue写一个程序,要求输入名字和口令。在一个新的文件编辑窗口中输入以下代码,将程序保存为swordfish.py。

 while True:
     print('Who are you?')
     name = input()
❶     if name != 'Joe':
❷        continue
     print('Hello, Joe. What is the password? (It is a fish.)')
❸     password = input()
     if password == 'swordfish':
❹         break
❺ print('Access granted.')

如果用户输入的名字不是Joe❶,continue语句❷将导致程序执行跳回到循环开始处。再次对条件求值时,执行总是进入循环,因为条件就是True。如果执行通过了if语句,用户就被要求输入口令❸。如果输入的口令是swordfish,break语句运行❹,执行跳出while循环,打印Access granted❺。否则,执行继续到while循环的末尾,又跳回到循环的开始。这个程序的流程图参见图2-13。

陷在无限循环中?

运行这个程序,提供一些输入。只有你声称是Joe,它才会要求输入口令。一旦输入了正确的口令,它就会退出。

Who are you?
I'm fine, thanks. Who are you?
Who are you?
Joe
Hello, Joe. What is the password? (It is a fish.)
Mary
Who are you?
Joe
Hello, Joe. What is the password? (It is a fish.)
swordfish
Access granted.

图2-13 swordfish.py的流程图。打叉的路径在逻辑上永远不会执行,因为循环条件总是True


其他数据类型中的某些值,条件认为它们等价于True和False。在用于条件时,0、0.0和' '(空字符串)被认为是False,其他值被认为是True。例如,请看下面的程序:

如果用户输入一个空字符串给name,那么while语句的条件就会是True ❶,程序继续要求输入名字。如果numOfGuests不是0 ❷,那么条件就被认为是True,程序就会为用户打印一条提醒信息❸。

可以用not name != ' '代替not name,用numOfGuests != 0代替numOfGuests,但使用类真和类假的值会让代码更容易阅读。

在条件为True时,while循环就会继续循环(这是它的名称的由来)。但如果你想让一个代码块执行固定次数,该怎么办?可以通过for循环语句和range()函数来实现。

name = ''
while not name:❶
print('Enter your name:')
name = input()
print('How many guests will you have?')
numOfGuests = int(input())
if numOfGuests:❷
print('Be sure to have enough room for all your guests.')❸
print('Done')

“类真”和“类假”的值

在代码中,for语句看起来像for i in range(5):这样,总是包含以下部分:

让我们创建一个新的程序,名为fiveTimes.py,看看for循环的效果。

print('My name is')
for i in range(5):
    print('Jimmy Five Times (' + str(i) + ')')

for循环子句中的代码运行了5次。第一次运行时,变量i被设为0。子句中的print()调用将打印出Jimmy Five Times (0)。Python完成for循环子句内所有代码的一次迭代之后,执行将回到循环的顶部,for语句让i增加1。这就是为什么range(5)导致子句的5次迭代,i分别被设置为0、1、2、3、4。变量i将递增到(但不包括)传递给range()函数的整数。图2-14展示了fiveTimes.py程序的流程图。

图2-14 fiveTimes.py的流程图

运行这个程序时,它将打印5次Jimmy Five Times和i的值,然后离开for循环。

My name is
Jimmy Five Times (0)
Jimmy Five Times (1)
Jimmy Five Times (2)
Jimmy Five Times (3)
Jimmy Five Times (4)

也可以在循环中使用continue语句。continue语句将让for循环变量继续下一个值,就像程序执行已经到达循环的末尾并返回开始一样。实际上,只能在while和for循环内部使用continue和break语句。如果试图在别处使用这些语句,Python将报错。

作为for循环的另一个例子,请考虑数学家高斯的故事。当高斯还是一个小孩时,老师想给全班同学布置很多计算作业。老师让他们从0加到100。高斯想到了一个聪明办法,在几秒钟内算出了答案,但你可以用for循环写一个Python程序,替你完成计算。

❶ total = 0
❷for num in range(101):
❸     total = total + num
❹ print(total)

结果应该是5050。程序刚开始时,total变量被设为0。然后for循环执行100次total = total + num。当循环完成100次迭代时,0到100的每个整数都加给了total。这时,total被打印到屏幕上。即使在最慢的计算机上,这个程序也不用1秒钟就能完成计算。

(小高斯想到,有50对数加起来是100:1 + 99, 2 + 98, 3 + 97……直到49 + 51。因为50 × 100 是5000,再加上中间的50,所以0到100的所有数之和是5050。聪明的孩子!)

实际上可以用while循环来做和for循环同样的事,for循环只是更简洁。让我们用与for循环等价的while循环,重写fiveTimes.py。

print('My name is')
i = 0
while i < 5:
    print('Jimmy Five Times (' + str(i) + ')')
    i = i + 1

运行这个程序,输出应该和使用for循环的fiveTimes.py程序一样。

某些函数可以用多个参数调用,参数之间用逗号分开,range()就是其中之一。这让你能够改变传递给range()的整数,实现各种整数序列,包括从0以外的值开始。

for i in range(12, 16):
    print(i)

第一个参数是for循环变量开始的值,第二个参数是上限,但不包含它,也就是循环停止的数字。

12
13
14
15

range()函数也可以有第三个参数。前两个参数分别是起始值和终止值,第三个参数是“步长”。步长是每次迭代后循环变量增加的值。

for i in range(0, 10, 2):
    print(i)

所以调用range(0, 10, 2)将从0数到8,间隔为2。

0
2
4
6
8

在为for循环生成序列数据方面,range()函数很灵活。举例来说,甚至可以用负数作为步长参数,让循环计数逐渐减少,而不是增加。

for i in range(5, -1, -1):
    print(i)

运行一个for循环,用range(5, -1, -1)来打印i,结果将从5降至0。

5
4
3
2
1
0

Python程序可以调用一组基本的函数,这称为“内建函数”,包括你见到过的print()、input()和len()函数。Python也包括一组模块,称为“标准库”。每个模块都是一个Python程序,包含一组相关的函数,可以嵌入你的程序之中。例如,math模块有数学运算相关的函数,random模块有随机数相关的函数,等等。

在开始使用一个模块中的函数之前,必须用import语句导入该模块。在代码中,import语句包含以下部分:

在导入一个模块后,就可以使用该模块中所有很酷的函数。让我们试一试random模块,它让我们能使用random.ranint()函数。

在文件编辑器中输入以下代码,保存为printRandom.py:

import random
for i in range(5):
    print(random.randint(1, 10))

如果运行这个程序,输出看起来可能像这样:

4
1
8
4
1

random.randint()函数调用求值为传递给它的两个整数之间的一个随机整数。因为randint()属于random模块,必须在函数名称之前先加上random.,告诉python在random模块中寻找这个函数。

下面是import语句的例子,它导入了4个不同的模块:

import random, sys, os, math

现在我们可以使用这4个模块中的所有函数。本书后面我们将学习更多的相关内容。

import语句的另一种形式包括from关键字,之后是模块名称,import关键字和一个星号,例如from random import *。

使用这种形式的import语句,调用random模块中的函数时不需要random.前缀。但是,使用完整的名称会让代码更可读,所以最好是使用普通形式的import语句。

要介绍的最后一个控制流概念,是如何终止程序。当程序执行到指令的底部时,总是会终止。但是,通过调用sys.exit()函数,可以让程序终止或退出。因为这个函数在sys模块中,所以必须先导入sys,才能使用它。

打开一个新的文件编辑器窗口,输入以下代码。保存为exitExample.py:

import sys

while True:
    print('Type exit to exit.')
    response = input()
    if response == 'exit':
        sys.exit()
    print('You typed ' + response + '.')

在IDLE中运行这个程序。该程序有一个无限循环,里面没有break语句。结束该程序的唯一方式,就是用户输入exit,导致sys.exit()被调用。如果response等于exit,程序就会中止。因为response变量由input()函数赋值,所以用户必须输入exit,才能停止该程序。

通过使用求值为True或False的表达式(也称为条件),你可以编写程序来决定哪些代码执行,哪些代码跳过。可以在循环中一遍又一遍地执行代码,只要某个条件求值为True。如果需要跳出循环或回到开始处,break和continue语句很有用。

这些控制流语句让你写出非常聪明的程序。还有另一种类型的控制流,你可以通过编写自己的函数来实现。这是下一章的主题。

1.布尔数据类型的两个值是什么?如何拼写?

2.3个布尔操作符是什么?

3.写出每个布尔操作符的真值表(也就是操作数的每种可能组合,以及操作的结果)。

4.以下表达式求值的结果是什么?

(5 > 4) and (3 == 5)
not (5 > 4)
(5 > 4) or (3 == 5)
not ((5 > 4) or (3 == 5))
(True and True) and (True == False)
(not False) or (not True)

5.6个比较操作符是什么?

6.等于操作符和赋值操作符的区别是什么?

7.解释什么是条件,可以在哪里使用条件。

8.识别这段代码中的3个语句块:

spam = 0
if spam == 10:
    print('eggs')
    if spam > 5:
        print('bacon')
    else:
        print('ham')
    print('spam')
print('spam')

9.编写代码,如果变量spam中存放1,就打印Hello,如果变量中存放2,就打印Howdy,如果变量中存放其他值,就打印Greetings!

10.如果程序陷在一个无限循环中,你可以按什么键?

11.break和continue之间的区别是什么?

12.在for循环中,range(10)、range(0, 10)和range(0, 10, 1)之间的区别是什么?

13.编写一小段程序,利用for循环,打印出从1到10的数字。然后利用while循环,编写一个等价的程序,打印出从1到10的数字。

14.如果在名为spam的模块中,有一个名为bacon()的函数,那么在导入spam模块后,如何调用它?

附加题:在因特网上查找round()和abs()函数,弄清楚它们的作用。在交互式环境中尝试使用它们。


相关图书

深度学习的数学——使用Python语言
深度学习的数学——使用Python语言
动手学自然语言处理
动手学自然语言处理
Python高性能编程(第2版)
Python高性能编程(第2版)
图像处理与计算机视觉实践——基于OpenCV和Python
图像处理与计算机视觉实践——基于OpenCV和Python
Web应用安全
Web应用安全
Python数据科学实战
Python数据科学实战

相关文章

相关课程