目录
文章目录
第一章 编程之道
这本书的目的是教会你怎样像一个电脑科学家一样思考。这种思考方式包含了数学、工程和自然学科的特点。和数学家一样,计算机科学家使用规范的语言来表达思想(具体的计算)。像工程学家一样,他们设计事物,把组件组装成系统,评估可选方案并作出权衡。像自然科学家一样,他们观察复杂系统的行为,作出假设并验证猜测。
一个计算机科学家最重要的技能就是解决问题,解决问题表示能够阐述问题, 创造性的考虑解决方案,并准确而清晰的表达出来。学习编程是一个锻炼解决问题能力的很好的机会。这就是为什么这章叫做编程之道。
某种层面上来说,学会编程本身就是一样很有用的技能。另一层面上讲,你将使用编程作为达到目的的手段,在我们学习的过程中,这一目的将会变得更加明确。
1.1 什么是程序
程序是一系列指定如何计算的特定的指令序列。要计算的可以是一些数学问题,比如求解一个方程组或者一个多项式的根;也可以是符号运算,比如文本查找和替换;也可以是图表处理,比如图像处理或者视频播放。
也许不同的语言细节不同,但都有输入输出、数学运算、条件结构、循环结构等基本结构。不管相信与否,每种语言,不管多复杂,都由这些基本结构组成。所以可以把编程理解为一种把大的复杂的任务分解为一个个很小的子任务,直到这些子任务小到能被这些基本结构所执行的一个过程。
1.2 第一个程序
按照传统,使用新语言编写的第一个程序是“Hello World!”,它所做的只是显示“Hello World!”这几个字符,在Python中,它可以这样实现:
>>> print('Hello, World!')
这是打印语句的一个示例,尽管它不打印任何东西,只是把结果显示在屏幕上,本例中,显示结果为:
Hello, World!
单引号中的内容就是要显示的文本,它本身并不显示在结果中。
圆括号表示print是一个函数,这在第三章中详细讲解。
1.3 数学操作符
Python中提供一些特殊的符号,称为操作符,来进行加法和减法这样的运算。
操作符+,-,*分别代表加减乘运算,示例如下:
>>> 40 + 2
42
>>> 43 - 1
42
>>> 6 * 7
42
操作符 / 表示除法:
>>> 84 / 2
42.0
之所以这里是 42.0 而不是42,我们在下一节中解释。操作符 ** 表示次方运算:
>>> 6**2 + 6
42
一些语言中,使用^作为次方运算,但 Python 中它表示位运算符,叫做 XOR。比如:
>>> 6 ^ 2
4
本书中对位操作符不做介绍,具体可参阅 http://wiki.python.org/moin/BitwiseOperators。
1.4 值和类型
像字符和数字这种量是程序运行所需要的最基本的东西,到目前为止我们所见到的量包括2, 42.0, “hello world!”。
这些量属于不同的类型:2是整整数,42.0是浮点数,“hello world!”是一个字符串。
当不确定一个量所属的类型时,可以在Python解释器中查询:
>>> type(2)
<class 'int'>
>>> type(42.0)
<class 'float'>
>>> type('Hello, World!')
<class 'str'>
在上述结果中,关键字“class”表是类别的意思。每种类型都是一类值的一个分分类。毫无疑问,整数属于 int 类型,字符串属于 str 类型,浮点数属于 float 类型。
那像 '2’和 '42.0’这种量呢?它们看起来像数数值,但又和字符串一样在引号中。
>>> type('2')
<class 'str'>
>>> type('42.0')
<class 'str'>
它们是字符串。
第二章 变量、表达式和语句
编程语言一个最强大的特性就是能操作变量variables,变量用来指代一个值。
2.1 赋值语句
赋值语句创建一个新的变量并且给它赋一个初始值:
>>> message = 'and now for something completely different'
>>> n = 17
>>> pi = 3.141592653589793
这个例子中有三个赋值语句,第一个将一个字符串赋值给一个名为message的变量,第二个将整数17赋值给变量n,第三个将π的值赋给pi。
通常在纸上表示一个变量时,写下它的名字然后用一个箭头指向它的值。这种图就叫做状态图,因为它展示了一个变量的内容。图2.1是上例的状态图。
图2.1 状态图
2.2 变量名
程序员通常给变量取一些有明确意义的名字,表明变量的用途。变量名可以由字母和数字组成,长度可以随意,但要注意不能以数字开头。使用大写字母变量名是合法的,但一般都使用小写字母。
下划线_,也可以出现在变量名中,一般用于连接由多个单词组成的变量名,如:your_name或者airspeed_of_unladen_swallow。
当你给一个变量一个非法的变量名时,会提示你出现语法错误。
>>> 76trombones = 'big parade'
SyntaxError: invalid syntax
>>> more@ = 1000000
SyntaxError: invalid syntax
>>> class = 'Advanced Theoretical Zymurgy'
SyntaxError: invalid syntax
76trombones以数字开头,所以非法,more@包含非法字符串@。 那class为什么也是非法的?
因为class是一个Python的关键字。解释器通过关键字来辨识程序的结构,所以关键字不能用作变量名。
Python3的关键字包含:
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 |
你没有必要记住表格中的全部内容,大多数开发环境中,都会以一种不同的颜色来显示关键字,所以当你想以一个关键字作为变量名时,你会知道的。
2.3 表达式和语句
一个表达式是变量、值和操作符的结合。一个单独的值也是一个合法的表达式,一个单独的变量也是。以下都是合法的表达式:
>>> 42
42
>>> n
17
>>> n + 25
42
当你在提示符后面输入表达式之后,解释器会计算它的值。在上面的例子中,n的值是17,n+25的值是42 。
语句是具有效果的代码单元,如创建变量或显示值:
>>> n = 17
>>> print(n)
第一行是一个定义语句,赋给n一个值,第二条语句是一个打印语句,将n的值显示出来。
当你输入一条语句后,解释器会执行它。一般,一条语句是没有值的。
返回目录
2.4 脚本模式
截至目前位置,我们一直使用交互模式运行python,直接和解释器进行交互。交互模式是一种很好的入门方式,但是如果使用的代码很多时就显得很笨拙。
另一种方法是将代码保存在一个脚本文件中,然后以脚本模式运行解释器来执行脚本。一般Python脚本的名称以.py
结尾。
返回目录
2.5 操作符优先级
当表达式包含多个运算符时,求值的顺序取决于操作符的优先级。对于数学运算符,Python遵循数学约定。首字母缩写PEMDAS 是一种记住规则的有用方法:
- (Parentheses)圆括号具有最高的优先级,可用于强制表达式按所需的顺序求值。由于优先计算括号中的表达式,所以
2 *(3-1)= 4,(1+1)**(5-2)= 8。
还可以使用括号使表达式更易于阅读,如(minute * 100) / 60,
。 - (Exponentiation)幂次方具有第二优先级,所以
1 + 2**3
是9
,而不是27
,2* 3**2
是18
,而不是36
。 - (Multiplication)乘法和(Division)除法的优先级高于(Addition)加法和(Subtraction)减法。所以
2*3-1 = 5
,不是4
,6+4/2 = 8
,不是5
。 - 具有相同优先级的运算符从左到右计算(除幂运算外)。所以在
度 / 2 * pi
的表达式中,先计算除法,结果再乘以pi
。要除以2pi
,可以用圆括号或者 写度/ 2 / pi
。
不用刻意去记运算符的优先级。如果看不明白一个表达式,就用括号把它写清楚。
返回目录
2.6 字符串操作符
一般来说,不能对字符串执行数学运算,即使字符串看起来像数字,所以下面的操作是非法的:
'2' - '1' 'eggs' / 'easy' 'third' * 'a charm'
但是有两个例外,+
和*
。
+
运算符执行字符串连接,它通过将字符串首尾相接来合并字符串。例如:
>>> first = 'throat'
>>> second = 'warbler'
>>> first + second
throatwarbler
*
运算符也适用于字符串,它执行字符串重复。例如,“Spam” * 3
是 “SpamSpamSpam”
。如果一个操作数是字符串,那么另一个必须是整数。
2.7 注释
随着程序变得越来越大、越来越复杂,也越来越难读懂。形式化的语言是密集的,通常很难查找一段代码并理解它在做什么,或者为什么这么做。
因此,在程序中添加注释以用自然语言解释程序在做什么非常重要。这些注释称为comments,以#
符号开始:
# compute the percentage of the hour that has elapsed
percentage = (minute * 100) / 60
在本例中,注释单独出现在一行上。你也可以在一行的末尾加上注释:
percentage = (minute * 100) / 60 # percentage of an hour
从#
到行尾的所有内容都被忽略——它对程序的执行没有影响。
注释在记录代码的不明显特性时最有用。像这个注释在代码中是冗余的,并且毫无用处:
v = 5 # assign 5 to v
而这个注释包含了代码中没有的有用信息:
v = 5 # velocity in meters/second
好的变量名可以减少对注释的需要,但是长名称会使复杂的表达式难于阅读,因此需要进行权衡。
第三章 函数 function
程序中,函数是执行计算的特定语句序列。定义函数时,要指定函数名称和语句序列。这之后,就可以按名称调用函数。
3.1 函数调用
我们已经看到一个函数调用的例子:
>>> type(42)
<class 'int'>
函数的名称是type
。括号中的表达式称为函数的参数()。对于这个函数,函数的返回结果是参数的类型。
通常说函数“接受”一个参数并“返回”一个结果。结果也称为返回值90。
3.2 数学函数
3.3 组合
到目前为止,我们已经单独研究了程序的元素——变量、表达式和语句,但没有讨论如何组合它们。
编程语言最有用的特性之一是它们能够使用小的结构并将它们组合起来。例如,函数的自变量可以是任何一种表达式,可以包函算术运算符,甚至函数:
x = math.sin(degrees / 360.0 * 2 * math.pi)
x = math.exp(math.log(x+1))
几乎在任何可以放置值的地方,都可以放置任意表达式,但有一个例外: 赋值语句的左侧必须是变量名。左边的任何其他表达式都是语法错误,如:
>>> minutes = hours * 60 # right
>>> hours * 60 = minutes # wrong!
SyntaxError: can't assign to operator
3.4 添加新函数
3.5 定义及使用
3.6 执行流
3.7 参数和变量
3.8 变量和参数是局部的
3.9 堆栈表
3.10 有效函数和无效函数
第四章 案例学习:界面设计
这本书的目的是教会你怎样像一个电脑科学家一样思考。
4.1 turtle模块
检查是否已经安装了turtle模块,可以在解释器中输入:
>>> import turtle
>>> bob = turtle.Turtle()
运行这句代码的时候,应该创建一个新的窗口,窗口中间有一个代表小乌龟的箭头。然后关掉这个窗口。
创建一个名为myploygon.py的文件,输入以下代码:
>>> import turtle
>>> bob = turtle.Turtle()
>>> print(bob)
>>> turtle.mainloop()
第二句代码利用turtle模块中Turtle函数创建了一个Turtle对象,并赋给了名为bob的变量。当打印bob时会显示:
<turtle.Turtle object at 0x0375A3B0>
表示bob指代一个在turtle模块中定义的Turtle对象。
mainloop告诉窗口等待用户的操作,这里我们什么都不做,直接关掉窗口。
##3.2 数学函数
##1.3 第一个程序
##1.4 数学操作符
4.2 简单的循环
4.3 封装
4.4 泛化
4.5 界面设计
4.6 重构
4.7 开发计划
4.8 文档字符串
4.9 调试
第五章 条件语句和递归
这本书的目的是教会你怎样像一个电脑科学家一样思考。
5.1 地板除和取模
检查是否已经安装了turtle模块,可以在解释器中输入:
>>> import turtle
>>> bob = turtle.Turtle()
运行这句代码的时候,应该创建一个新的窗口,窗口中间有一个代表小乌龟的箭头。然后关掉这个窗口。
创建一个名为myploygon.py的文件,输入以下代码:
>>> import turtle
>>> bob = turtle.Turtle()
>>> print(bob)
>>> turtle.mainloop()
第二句代码利用turtle模块中Turtle函数创建了一个Turtle对象,并赋给了名为bob的变量。当打印bob时会显示:
<turtle.Turtle object at 0x0375A3B0>
表示bob指代一个在turtle模块中定义的Turtle对象。
mainloop告诉窗口等待用户的操作,这里我们什么都不做,直接关掉窗口。
5.2 布尔表达式
5.3 逻辑运算符
5.4 条件执行
5.5 选择执行
第六章 有返回值的函数
第七章 迭代
这章讲述迭代,表示能够重复运行的代码块。5.8章讲到的递归就是一种迭代,4.2章中讲到的for循环是另一种迭代形式。这一章中我们讲述用while语句表示的迭代。但首先我们讲一下变量的定义。
7.1再赋值
正如你所见,对同一个变量赋值多次是合法的。新的赋值操作使已经存在的变量指向新的值。
>>> x = 5
>>> x
5
>>> x = 7
>>> x
7
第一次我们打印x,他的值为5,而第二次其值为7 。
这里我解释一个普遍的疑惑。python中使用等号=来进行赋值操作,像a = b这样的语句很容易被理解为等价的数学命题,即声明a和b相等。但这种理解是不正确的。
首先,相等是一种对称的关系而赋值不是。
第八章 字符串
第九章 案例学习:单词游戏
本章我们进行第二个案例学习,涉及到如何搜索具有特定特性的单词来解决填词问题。比如说我们找英文中最长的回文并指出其中字母按照字母表顺序排列的单词。我们会通过编程来简化这个问题
9.1 读取单词列表
本章的练习中我们需要一系列英文单词。网有很多的单词列表,但最适合我们的还是Moby lexicon项目中的一个单词列表,由Grady Ward收集并贡献给公共领域。这个列表的单词来自113,809 份官方填字谜游戏。也就是说,这些单词在填字谜和其他游戏中被认为是有效的。Moby 收集的单词列表113809of.fic 你可以在http://thinkpython2.com/code/words.txt 下载一份拷贝,并命名为words.txt。
这个文件是一个纯文本文件,可以用文本编译器打开,当然你也可以使用Python来读取它。内置的open函数以文件名为参数并返回一个file对象用以读取文件。
>>> fin = open('words.txt')
fin是用以输入的文件对象的一个一般名称,文件对象提供一些用以读入的方法,比如 readline
用以读取一行字符并返回一个字符串:
>>>fin.readline()
'aa\n'
这个列表中第一个单词是’aa’,是一种 ,\n表示换行。
文件类指针会保持它在文件中所在的位置,所以当你再次调用 readline
你会得到新的一行:
>>>fin.readline()
'aah\n'
9.2 搜索
9.2
第十章 列表
第十一章 字典
第十二章 元祖
第十三章 案例学习:数据类型的选择
第十四章 文件
本章介绍将数据永久保存的编程概念以及如何使用文件和数据库等不同的永久存储介质。
14.1 持久性
到目前为止我们见到的程序从某种意义上来讲都是暂时的,这些程序只运行很短时间,产生一些输出,但程序运行结束后所有数据都会消失掉。再次运行时又从零开始。
而有一些程序是持久的,它们运行很长那个时间,或者一直运行,它们至少会保存一些它们的数据到硬盘等这样永久性的存储介质。当它们被关闭或者重新开始时它们可以从上次停止的地方继续运行。
这样持久性的程序有操作系统,只要电脑开机操作系统就一直运行;还有网络服务器,它需要一直运行来等待接入网络的请求。
程序保存自己数据一个最简单的方法是读取和写入文本文件。我们已经见了很多从文本文件中读取数据的程序,本章就学习如何将数据写入文本文件。
也可以将程序状态储存到数据库中,本章也会介绍一个简单的数据库和模块,pickle,会让存储程序数据十分简单。
14.2 读和写操作
一个文本文件是一个存储在硬盘、闪存等永久存储介质上的字符序列。在9.1节中已经介绍了如何打开一个文件并读取数据。
14.3 格式符
写的变量必须是一个字符串string类型。
14.4 文件名和路径
14.5 异常捕获
14.6 数据库
##14.
14.8 管道
14.9 写入操作
14.10 调试
第十五章 类和对象
到了这里你已经知道如何使用函数组织代码,如何用内置类型组织数据。接下来就要学习面向对象编程,用用户定义的类型来组织代码和数据。面向对象编程是一个很大的话题,需要讲几个章节。
本章节的示例代码可以在这里下载,练习题的答案也可以在这里这里查看。
第十六章 类和函数
第十七章 类和方法
17.1 面向对象特征
17.2 print 对象
17.3 另一个示例
17.4 复杂示例
17.5 init 方法
17.6 __str__方法
17.7 运算符重载
第十八章 继承
第十九章 补充
附录A 调试
程序中常见错误有三种: 语法错误(syntax errors)、运行时错误(runtime errors)和语义错误(semantic errors)。为了更快地找到它们,区分它们是有用的。
Syntax error:
“Syntax”是指程序的结构及其规则。例如,圆括号必须成对出现,所以
(1 + 2)
是合法的,而8)
是语法错误。
如果程序中任何地方出现语法错误,Python将显示错误消息并退出,您将无法运行程序。
Runtime error:
第二类错误是运行时错误,之所以称为运行时错误,是因为该错误运行程序时才出现。这些错误也称为异常(exceptions), 因为它们通常表示发生了异常事件。
Semantic error:
语义错误与程序意义相关。如果程序中存在语义错误,它将在不生成错误消息的情况下运行,但不会产生正确结果。
识别语义错误可能比较棘手,因为它要求您通过查看程序的输出并找出它在做什么来调试。
附录B 算法分析
附录C 词汇索引
- portability: 可移植性
- interpreter: 解释器
- prompt: 提示符
- token: 程序语法结构中最基本的元素,就像自然语言中的单词。
- syntax: 语法
- parse: 检查程序并分析语法