PDF源文件地址 : http://www.greenteapress.com/thinkpython/thinkpython.pdf
[自述:翻译第一章花了我大概一周的时间,虽然不是7*8小时的那种翻译强度,只是工作之余挑战一下自己,让我体会到翻译就跟本章讲的调试一样,它会让你抓狂,郁闷,沮丧。但是最终把这一章翻译完成了,虽然自我感觉翻译得不是很好,但还是蛮有收获感的,有点挑战自我的意思。目前离翻译完成还有18章!!!]
第一章
编程之道
本书的目的在于教你如何像一个计算机科学家思考。这种思维方式结合了像数学,工程学和自然科学的一些有效的特性,就像数学家或者计算机科学家一样用正式语言去表达想法(尤其是算法)。让你能够像工程师那样设计并安装组件到系统里,并且在众多方案中评估选择。或者像科学家那样观察复杂的系统行为,建立假设并验证其真伪。
对于计算机科学家来说最重要一项技能就是解决问题。解决问题意味着提出问题,创造性的思考解决方案,并且清楚准确的表达出解决方案。事实证明,学习编程的过程就是锻炼解决问题能力的绝佳机会,所谓熟能生巧。这就是本章的标题为“编程之道”的原因。
一方面,学会编程是一项有用的技能。另一方面,运用编程可以达成目标。随着我们继续深入的学习,你会发现这个目标会越来越明确。
- 1.1 Python编程语言
Python是高级语言的一种,或许你还听说过其它的一些高级语言,例如 C, C++, Perl 和 Java 等。当然也有一些低级语言,通常是指所谓的“机器语言”或者“汇编语言”。简单来说,计算机只能运行低级语言写的程序。因此高级语言写的程序运行之前需要处理,这额外的处理是需要时间的,这是高级语言的一个小瑕疵,但是瑕不掩瑜。首先,使用高级语言编程相对容易一些,你只要花很少的时间就能写出更短,可读性更强的代码,也很容易修正;其次,高级语言的可移植性很强,也就是说可以跨平台运行(在某些平台只需做很小的修改,甚至不需要修改),这是低级语言所不具备的特性(如需跨平台运行必须重写)。
由于高级语言的这些优势,几乎所有的程序都是用高级语言编写,只有很少特定的程序还在使用低级语言。
有两种程序可以把高级语言转换为低级语言:解释器和编译器。解释器读取高级语言并且按程序逻辑执行,并且一次只能执行一个程序,逐行读取程序并处理运算。解释器结构如图1.1所示。
图1.1
编译器则在程序运行之前读取并转换。高级程序称之为源码,转换器称之为目标代码或可执行程序。程序只需要编译一次,后面可以重复执行无需其他处理。编译器结构如图1.2所示。
图1.2: 编译器把源码转换成目标代码, 并由硬件执行器运行.
Python程序是由解释器来执行的,因此它是一种解释型语言。它支持两种模式:交互式模式和脚本模式。交互式模式下,你输入Python代码,解释器则输出结果:
>>> 1 + 1
2
“>>>” 是表示Python解释器就绪的提示符,如果你输入 1+1,解释器则会输出结果2.
或者,你可以把代码保存在文件里,再使用解释器来执行文件,这就是脚本模式。按照惯例,Python脚本的后缀为.py。
执行脚本必须提供完整的文件名给解释器。例如脚本文件名为 dinsdale.py 在UNIX命令窗口你需要输入 python dinsdale.py 即可。在其它开发环境运行脚本会有所不同,你可以从Python官网http://python.org查找对应的说明文档。
交互模式适用于测试较少代码的情况,代码较多的情况建议采用脚本模式,这样方便你随时修改和执行。
- 1.2 什么是程序?
程序是为实现特定目标或解决特定问题而编写的命令和指令。可能是解决数学问题,例如求方程式或求多项式的根,当然也可能是符号计算,例如搜索或替换文档里的文字或者编译程序。
不同编程语言会有一些差异,但是所有语言都有相似之处:
- 输入:
- 从键盘输入,从文件读取,或者从其它设备输入
- 输出:
- 显示结果在屏幕上,保存在文件,或者输出到其它设备
- 数学:
- 提供基础数学运算,例如加法和乘法
- 条件判断:
- 验证条件并执行相应的代码
- 循环:
- 提供一些循环操作,通常用来处理一些重复变化
信不信由你,这些就是编程语言的全部!不管你用过的程序有多么复杂,它都基本都是由这些指令所组成。因此,你可以把编程理解为:把一个很大很复杂的任务拆分成很多小的子任务,直至简单到可以用这些基本的指令来执行为止。此时概念可能会有点模糊,不过待后面我们探讨到算法的时候,我们还会回到这个话题上来的。
-
1.3 什么是调试?
编程是容易出错的。程序错误称之为Bug,而跟踪除错的过程称之为调试。
3种类型的错误可以在程序里抛出:语法错误,运行时错误和语义错误。调试过程中区分这些错误类型可以让你很快的消除错误。
-
1.3.1 语法错误
如果没有语法错误,Python就可以运行脚本。否则,解释器就会抛出错误信息。语法是指程序结构和语法结构的规则。例如,括号必须是成对出现,如(1+2)是合法的,而缺一个括号,如 8) 则是是语法错误。
日常阅读的时候,读者往往可以忍受很多语法错误,因此我们可以无障碍阅读E.E.卡明斯的诗歌。而Python则不同,只要程序中存在任何语法错误,它都会抛出错误信息并终止运行。因此在学习编程的初期,你可能会花费较多的时间去跟踪语法错误,但随着经验的增长,发现和解决语法错误的能力会随之增强。
-
1.3.2 运行时错误
第二种类型是运行时错误,顾名思义,这种错误是发生在程序运行的过程中的,也可以称为异常。通常表示运行过程中的突发情况。本书的前几章的范例中很少有这种错误。
-
1.3.3 语义错误
第三种就是语义错误。如果程序中有语义错误,虽然程序成功运行起来,并且不会产生任何错误信息。但事实上程序并没有按你所想的那样去执行指令。
也就是说,程序并非按你的本意去运行的,你写的程序在语义上是不对的。判断语义错误相对比较麻烦,此时你只能通过查看程序的输出结果来搞清楚程序是如何执行指令的。
- 1.3.4 实验调试
对程序员而言,调试能力是最重要的能力之一。虽然调试的过程会让你抓狂,但是你不得不承认调试的过程很有挑战性,这恰恰也是编程最大的乐趣。
在某种程度上,调试过程就像侦探破案一样。各种线索摆在眼前,然后你再推断出导致结果的事件和过程。
调试也像一门实验科学,一旦你发现程序有问题,你会不断的尝试修改和测试。如果假设是正确的,那测试结果也一定是在预测范围之内,反之亦然。正如夏洛特.福尔摩斯所说:“当你排除了所有的不可能,剩下的不管有多么不可能,那一定就是真相。”(柯南道尔,《四人的标志》)
对有些人来说,编程就是调试。也就是说,编程就是一个逐步调试的过程,直至程序完成你让它做的事情。在我看来,你可以从头开始做一个程序,逐步修改,调试,直至做完一个完整的程序。
例如,Linux是一个开源操作系统,它的核心代码有上千行,不可思议的是,Linux是由Linus Torvalds编写的一个探索英特尔80386芯片的一个简单程序。据拉里.格林菲尔德所说,“Linus 早期做的这个项目,最初只是为了切换打印内容,并最终演化成现在的Linux系统。”(Linux用户指南 测试版1)
在后面的章节里面,我将会对调试和编程实战提出更多的建议。
- 1.4 正式语言和自然语言
自然语言:人们日常交流的语言,如英语,西班牙语,法语等等。这完全是自然流露,无需刻意修饰的语言。
正式语言:人们为特定程序设计的语言。例如,数学家在计算中表示数字与符号之间关系的语言就是正式语言;化学家用分子式表示化学结构也是正式语言。因此最重要的是:
-
- 编程语言是设计出来处理运算的正式语言。
正式语言有严格的语法要求。例如:3+3=6是正确的算式语句,而3+=3$6则是错误的。
语法规则有两种风格,涉及到符号和结构。符号是语言的基本元素,如单词,数字,化学元素等。3+=3$6之所以是错误的是因为$在数学计算中不是一个合法的符号(至少我是这么认为的)。
第二种语法规则也与语句的结构有关,也就是说,符号是按序排列的,3+=3这个语句不对是因为即使+和=是合法的符号,但是却不能把两个符号同时放在一起使用(此处存疑,因为C语言中是支持x+=1这种写法的)。
- 练习1
写一条语法结构良好但是包含非法符号的句子,然后在写下一条结构非法但是没有非法符号的句子。
当你用正式语言阅读的时候,你必须弄清楚句子的结构(尽管使用自然语言的时候是无意而为的),这个过程叫做解析。
举个例子,当你听到“The penny dropped”,你会想到“The penny”是主语,“dropped”是谓语,当你解析完整个句子,你才会明白这句话的意思或语义。假设你知道“penny”和“drop”是什么意思,那么这句话的意思你就基本清楚了。(字面意思是“钱掉了”,penny有“便士”的意思,习语表示“恍然大悟”的意思)
尽管正式语言和自然语言有很多共同点,例如符号,结构,语义和语法等,但是它们依然有区别:
歧义:
自然语言是具有模糊特性的,人类在使用自然语言的时候是会参考上下文的语境以及其他相关信息的。而正式语言则不允许有任何歧义,它不管上下文的内容,每一句话只表达一种含义。
冗余:
为了减少歧义并且避免误解,自然语言包含了很多冗余信息,因此,自然语言有点啰嗦,而正式语言则更加简洁。
文字:
自然语言里面包含了很多习语和比喻。正如我前面说的“The penny dropped”,并不是说钱掉了(习语是说某人迷惑之后突然意识到了,有恍然大悟的意思),正式语言则按字面含义来理解。人类使用自然语言肯定无法适应正式语言的这种特性。从某些方面来说,自然语言和正式语言之间的差异就想诗歌和散文一样,但不仅仅如此:
诗歌:
诗歌不仅意境深远而且读起来押韵唯美,主要是为了表达作者要抒发的情感,不仅晦涩难解,甚至字里行间刻意为之!
散文:
散文则侧重表达字面含义,字里行间能提供更多内容,比诗歌更容易分析,但是也有词不达意的情况。
程序:
计算机程序是没有任何歧义的,是可以通过符号和结构的分析完全理解的。
这里有一些阅读程序的建议(包含其它的正式语言):首先,阅读正式语言比阅读自然语言需要更多的时间。另外,结构也很重要,并不是说从头到尾阅读一字不落的阅读就能有助于理解。相反,在脑子里直接解析程序结构和内容更有帮助。最后,细节决定一切,因为在自然语言中小的拼写错误,发音错误是可以忽略的,但是正式语言则不然。
- 1.5 第一个程序
按照惯例,第一个程序通常称之为“Hello, World!”,就是在屏幕上输出文字“Hello, World!”,Python里面只要这一句就可以:
print 'Hello, World!'
这是print用法,不是实际意义上的把内容打印在纸张上面,而是把内容打印在显示器上,此时你可以在电脑屏幕上看到:
Hello, World!
程序中的引号是不显示在打印内容里面的,在Python3版本里,语法略有不同:
print('Hello, World!')
“()”在这里说明print是一个函数,在后面第3章会介绍到函数的内容。在本书都将采用print语句,而不是print函数,如果你用的是Python3版本,请自行转换,除此之外再无分歧。
- 1.6 调试
如果你在学习本书内容的时候恰好在电脑旁边,那将是非常好的,你可以选择交互模式,即时运行代码,如果你采用脚本模式那也是非常好的。
尝试着犯错有助于你了解语言的新特性,例如:把“Hello, World!”程序里的引号去掉一个,或者两个引号都去掉,又或者把print拼写错了将会导致什么结果?这种类型的实验不仅可以帮助你记住所学的内容,而且可以帮助你调试,因为在这个过程中你将了解错误消息的含义。在这个阶段故意犯错误比无意中犯错要好。
编程的过程中,尤其是在调试的时候往往会让你情绪激动,甚至与大BUG纠缠的时候你会感到愤怒,沮丧并且难堪。
事实证明,人对电脑的反应跟人对人的反应是一样的。当电脑运行良好的时候,我们会把他们当成队友,当电脑表现粗鲁或者固执的时候,我们对他们的态度也是一样。
预先了解电脑的这些特性或许对你有帮助,一是把电脑当成一个具有精确计算能力的员工,另外电脑有特定的缺陷,比如缺乏同理心没有大局观。你的工作就是做一个优秀的管理者,发挥电脑的优势,弱化电脑的缺陷,不要因为情绪而影响到工作效率。虽说调试工作会让人沮丧,但它的确是很有价值的一项技能,即使是对编程以外的事情也是很有价值的。在本书的每一章结尾都会有调式的部分,期望这部分内容能够帮助到你。
- 1.7 术语表
问题解决
表述问题,找到解决方案并表述解决方案的过程
高级语言
像Python这样为了便于人类读写而设计的语言
低级语言
为计算机执行而设计的语言:机器语言,汇编语言
可移植性
程序可以在不同平台运行的属性
解释
逐行翻译并执行高级语言编写的代码
编译
一次性把高级语言写的程序翻译成二进制指令序列,形成目标代码
源代码
用高级语言编写的,未经编译的字符流
目标代码
源代码经过编译器编译之后输出对象
可执行程序
目标代码的另一个表述:可执行文件
提示符
解释器显示的字符,表示可以接收用户的输入
脚本
存储在文件中的程序代码(通常需要解释器进行翻译)
交互模式
在Python解释器中输入代码并执行
脚本模式
用Python解释器读取文件中的代码并执行
程序
制定计算的指令集
算法
解决同类型问题的一般过程
Bug
程序中的错误
调试
发现并解释发现的3中错误类型
语法
程序结构
语法错误
程序中无法完成语法解析的错误
异常
程序运行中检测到的错误
语义
程序的含义
语义错误
程序中的一种错误,它让程序不按程序员的意图执行指令
自然语言
人类交流所使用的语言
正式语言
人类设计用于特殊目的,如表示数学思想或计算程序。所有程序语言都是正式语言。
符号
程序语法结构的基本要素之一,类似于自然语言里的单词。
解析
检查程序并分析其语法结构。
打印语句
用Python解释器在屏幕上显示值
- 1.8 练习
练习2
访问Python网站http://python.org 。这个页面包含了Python的信息以及和Python相关信息的页面,你能搜索到Python的文档。
例如,你在搜索框输入print,你看到第一个链接就是print语句的文档。搜索结果并非全都对你有帮助,但是聊胜于无。
练习3
打开Python解释器,输入help()即可打开在线帮助程序,或者直接输入help('print')可以获取到跟print
语句相关的信息。如果不行的话,你需要检查一下是否安装了Python或者检查一下环境变量,具体跟你的操作系统和Python版本有关。
练习4
打开Python解释器把它当成一个计算器。Python的数学计算语法几乎跟标准数学运算是一样的。例如+,-和/分别表示加,减和除,当然,正如你所料想的那样,乘法符号是 * 。如果参加一个10公里长跑花了43分钟30秒,那么你的时速是多少英里?(提示:1英里大约1.61公里)
print(10/((43*60+30)/60/60)/1.61)
计算结果:
8.567144998929107