户端,您可以在任意设备上,用自 己喜欢的浏览器和PDF阅读器进行 阅读。
但您购买的电子书仅供您个人使用,
未经授权,不得进行传播。
我们愿意相信读者具有这样的良知 和觉悟,与我们共同保护知识产权。
如果购买者有侵权行为,我们可能 对该用户实施包括但不限于关闭该 帐号等维权措施,并可能追究法律 责任。
本书包括 Python 程序设计的方方面面:首先,从 Python 的安装开始,随后介绍了 Python 的基础知识 和基本概念,包括列表、元组、字符串、字典以及各种语句;然后,循序渐进地介绍了一些相对高级的主 题,包括抽象、异常、魔法方法、属性、迭代器;此后,探讨了如何将 Python 与数据库、网络、C 语言等 工具结合使用,从而发挥出 Python 的强大功能,同时介绍了 Python 程序测试、打包、发布等知识;最后,
作者结合前面讲述的内容,按照实际项目开发的步骤向读者介绍了 10 个具有实际意义的 Python 项目的开 发过程。
本书内容涉及的范围较广,既能为初学者夯实基础,又能帮助程序员提升技能,适合各个层次的 Python 开发人员阅读参考。
定价:99.00元
读者服务热线:(010)51095186转600 印装质量热线:(010)81055316 反盗版热线:(010)81055315
广告经营许可证:京东工商广登字 20170147 号 著 [挪] Magnus Lie Hetland
译 袁国忠 责任编辑 杨 琳 责任印制 彭志环
人民邮电出版社出版发行 北京市丰台区成寿寺路11号 邮编 100164 电子邮件 [email protected]
网址 http://www.ptpress.com.cn 北京 印刷 开本:800×1000 1/16 印张:29.75
字数:703千字 2018年 2 月第 3 版 印数:223 001 — 231 000册 2018年 2 月北京第 1 次印刷
著作权合同登记号 图字:01-2017-4043号
◆
◆
◆
1
2
3
4
5
8
10
11 9 6
7
版 权 声 明
Original English language edition, entitled Beginning Python: From Novice to Professional, Third
Edition by Magnus Lie Hetland, published by Apress, 2855 Telegraph Avenue, Suite 600, Berkeley, CA94705 USA.
Copyright © 2017 by Magnus Lie Hetland. Simplified Chinese-language edition copyright © 2018 by Posts & Telecom Press. All rights reserved.
本书中文简体字版由Apress L. P.授权人民邮电出版社独家出版。未经出版者书面许可,不得 以任何方式复制或抄袭本书内容。
版权所有,侵权必究。
1
2
3
4
5
8
10
11 9 6
7
前 言
巨蟒剧团
①有首老歌唱道:“又来了一个,它走了又来。又来了一个,什么时候是个头?”自 本书第2版面世以来,Python 3的普及程度得到了极大提高,因此这一版完全转向了Python 3。在 此期间,还有其他的变化:在Python生态系统中,各种包轮番登场,各种编码实践大行其道后又 日渐式微。在必要之处或对读者有帮助的前提下,本书的有些部分完全重写,但最初的痕迹还依 稀可见。例如,本书前身Practical Python于21世纪初出版时,Usenet依然应用广泛,可现在大多 数互联网用户可能从未听说过它。因此,对于第23章创建连接到NNTP服务器的项目,与其说是 为了介绍主流编程生涯中将用到的编程技能,不如说是让读者了解一下历史。一些比较古怪的内 容也依旧保留,因为它们是很不错的编程示例,也是本书悠长历史的见证。
与以往一样,我要深深地感谢让本书以前各版得以付梓的人。这里我要特别感谢Mark Powers,感谢他在我进度缓慢时极具耐心。还要感谢Michael Thomas,感谢他出色的技术审阅工 作(并指出了原稿中所有的Python 2式
print语句)。但愿你喜欢全新升级后的这一版。正如Terny Jones在提到那首老歌时所说:“显然,有一个完整的管弦乐队会更好。”
第 2 版前言
新版的《Python基础教程》终于和大家见面了。如果算上本书的前身Practical Python,实际 上这已经是第3版了。这是我将近10年心血的结晶。在此期间,Python发生了很多有趣的变化,
我也尽力调整了对这门语言的介绍。当前,Python处于长期以来最为翻天覆地的转变期:Python 3 推出。编写本书期间,最终版本还未发布,但其包含的功能已确定,还推出了多个可用的版本。
这次修订不向后兼容,这是我编写这一版时面临的最大挑战。换而言之,我不能仅仅挑出一些新 增的功能进行介绍。另外,它还修改了一些既有的功能,因此有些在Python 2.5中理所当然的事 情在新版本中已不再正确。
倘若整个Python社区都立即转向Python 3并修改所有的遗留代码,那根本不成问题。我只需 介绍Python 3就行!然而,目前依然存在大量用Python 2编写的代码,而且大家可能还会继续编写 这样的代码,直到有一天所有人都认为应使用Python 3。
那么我是如何应对这种变化的呢?首先,虽然有些向后兼容的变化,但Python语言本身总体变
——————————
① Monty Python,英国的一个超现实幽默表演团体,喜剧界的披头士,在20世纪70年代风靡全球。Python语言的命 名来源于此。——编者注
化不大。因此,如果完全针对Python 2.5,编写的内容对Python 3来说也是大体正确的(对Python 2.6 来说更是如此)。至于那些不再正确的部分,我采取了比较保守的态度,因为大家完全接受Python 3 还需要一段时间。因此,这一版主要是基于Python 2.5编写的,同时指出将会改变的情形。另外,
我还提供了附录D,让你对重大变化有大致的了解。对大多数读者来说,这样处理是完全可行的。
编写这一版期间,有几个人帮了我很大的忙。与前两版(本书第1版以及之前的Practical Python)
一样,Jason Gilmore扶我上马,并在项目启动期间扮演了重要角色。项目启动后,Richard Dal Porto、
Frank Pohlmann和Dominic Shakeshaft不断伸出援手,保证项目得以顺利进行。在确保代码正确方 面,Richard Taylor居功至伟(倘若代码依然存在错误,责任在我),而Marilyn Smith出色地完成了 手稿润色工作。这里还要感谢Apress出版社的其他工作人员,他们是Liz Berry、Beth Christmas、
Steve Anglin和Tina Nielsen。另外,感谢包括Bob Helmbold和Waclaw Kusnierczyk在内的读者提出勘 误和宝贵的建议。最后,在本书前两版的出版过程中,很多人都提供了帮助,这里衷心地感谢他们。
第 1 版前言
几年前,Jason Gilmore找到我,让我为Apress出版社写本书。他看了我撰写的Python在线教 程,想让我写一本风格类似的书。我受宠若惊,既兴奋又有点紧张。我最关心的是,这需要多长 时间、对学业会有多大的影响,因为当时我正在读博士。结果表明,这是一项非常艰巨的任务,
花费的时间远远超出了预期。
所幸这对我的学业没有太大的影响,我按时获得了博士学位。
去年,Jason又找到我说,Apress出版社想让我对原书进行修订和扩充,不知道我是否有兴趣。
当时我正忙于熟悉新取得的副教授职位,而业余时间都花在了扮演Peer Gynt上,因此时间依然是 主要的问题。事情安排妥当并有更多业余时间后,我接受了这项任务。你可能猜到了,最终的结 晶就是你现在手捧的这本书。本书的大多数内容都来自Practical Python,但基于Python的最新变 化做了全面修订,同时新增了几章。另外,根据这一版的组织结构,调整了原来一些内容的位置。
很多读者对Practical Python提供了积极的反馈,但愿本版保留了读者喜欢的内容,同时新增的内 容也能得到读者青睐。
本书编写期间,有几个人不断地给予我帮助和鼓励。没有他们,本书根本不可能付梓。这里 对他们表示衷心的感谢。感谢本书编写期间直接与我协作的团队:Jason Gilmore让这个项目得以 上马并确保不偏离方向;Beckie Stones整理各种材料;Jeremy Jones和Matt Moodie提供了专业的 意见和见解;Linda Marousek对我极具耐心;还有其他成员让本书得以顺利出版。如果没有
Practical Python打下的基础,本书不会是现在这个样子。这里要感谢Jason Gilmore和Alex Martelli出色的技术编辑工作(Jason负责全书,Alex负责前半部分)以及本职之外的各种意见和建议。感 谢Erin Mulligan和Tory McLearn一路上领我前行,并在需要时让我调整方向。感谢Nancy Rapoport 对手稿进行润色。感谢Grace Wong回答他人无法回答的问题。感谢Pete Shinners就项目10的游戏 提供宝贵的建议。感谢多位读者的来信,这些来信给了我极大的鼓励!最后,感谢我的家人和朋 友,尤其是女朋友Ranveig在本书编写期间给予的宽容。
1
2
3
4
5
8
10
11 9 6
7
引 言
C程序犹如拿着剃刀在刚打过蜡的地板上劲舞。
——Waldi Ravens C++学起来很难,因为它天生如此。
——佚名 Java从很多方面来说,就是简化版的C++。
——Michael Feldman 接下来请欣赏与众不同的表演。
——巨蟒剧团之《飞翔的马戏团》
前面引用了别人的几句话,旨在为本书定下基调,就是不那么严肃正式。为让本书阅读起来 轻松愉快,我力图以幽默的方式来讨论Python编程这个主题。幽默是Python社区的传统,而这种 幽默在很大程度上与巨蟒剧团的短剧相关。因此,本书的有些示例看起来有点傻,但愿你能容忍。
[顺便说一句,Python来源于巨蟒剧团(Monty Python),而不是蟒蛇。]这里将简单地说说Python 是什么,为何要使用它,有哪些人在使用它,本书为谁而写,并概述本书的组织结构。
Python是什么?为何要使用它?官方宣传说:Python是一种面向对象的解释性高级编程语言,
具有动态语义。这句话中有很多术语,在阅读本书的过程中,你会逐渐了解其含义。这句话的要 点在于,Python是一种知道如何不妨碍你编写程序的编程语言。它让你能够毫无困难地实现所需 的功能,还让你能够编写出清晰易懂的程序(与使用当前流行的其他大多数编程语言相比,编写 出来的程序要清晰易懂得多)。
虽然Python的速度可能没有C、C++等编译型语言那么快,但它能够节省编程时间。仅考虑 到这一点就值得使用Python,况且对大多数程序而言,速度方面的差别并不明显。如果你是C语 言程序员,那么你可轻松地使用C语言实现程序的重要部分,再将其与Python部分整合起来。如 果你没有任何编程经验(并对我提及C和C++感到有点迷惑),那么简洁而强大的Python就是你进 入编程殿堂的理想选择。
那么,有哪些人在使用Python呢?从Guido van Rossum于20世纪90年代初创造这门语言起,
其追随者就在不断增加,最近几年尤其如此。Python广泛用于完成系统管理任务(例如,它是多 个Linux发行版的重要组成部分),也被用来向新手介绍编程。NASA使用它来完成开发工作,并 在多个系统中将其用作脚本语言;工业光魔公司在预算庞大的故事片中使用Python来制作特效;
Yahoo!使用它(以及其他技术)来管理讨论组;Google使用它实现了网络爬虫和搜索引擎的众多 组件。Python还被用于计算机游戏和生物信息等众多领域。不久后可能就会有人问:有谁不使用 Python呢?
本书是为有志于学习Python编程的人写的,适合从编程门外汉到计算机高手的各种读者阅 读。如果你没有任何编程经验,应从第1章开始阅读,阅读到看不懂的内容后,开始动手编写一 些程序。等到条件成熟后,再回过头来继续阅读更复杂的内容。
如果你熟悉编程,对有些基础知识可能并不陌生(但书中会不时出现令你意外的细节),因 此可大致浏览前几章,以便对Python的工作原理有大致认识。当然,也可通读附录A。它是根据 Python在线教程“Instant Python”改编而来的,让你能够快速了解最重要的Python概念。对它有 大致认识后,可直接跳到第10章,去学习Python标准库。
本书的最后10章是10个编程项目,展示了Python语言的各种功能。无论你是初学者还是专家,
都应该会对这些项目感兴趣。虽然对经验不那么丰富的程序员来说,最后几个项目理解起来有点 难,但阅读本书的前半部分之后,完全能够按说明完成这些项目。
这些项目涉及众多主题,掌握这些主题对你自己动手编写程序大有裨益。你将学习如何完成 一些现在看起来根本无法完成的任务,如创建聊天服务器、点对点文件共享系统和功能齐备的图 形计算机游戏。这些任务乍一看好像很难,但最终你将发现,它们实际上大多容易得难以置信。
如果你想下载源代码,可访问Apress网站(http://www.apress.com)
①。
就说这么多。冗长的引言总是让我觉得有点烦,现在就开始Python编程吧——从第1章或附 录A开始。祝你好运,编程愉快!
——————————
① 图灵社区本书页面也提供源代码下载:ituring.com.cn/book/2118。——编者注
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
18 17
目 录
第 1 章 快速上手:基础知识
... 11.1 交互式解释器 ... 1
1.2 算法是什么 ... 2
1.3 数和表达式 ... 3
1.4 变量 ... 5
1.5 语句 ... 5
1.6 获取用户输入 ... 7
1.7 函数 ... 8
1.8 模块 ... 8
1.8.1 cmath和复数 ... 9
1.8.2 回到未来 ... 10
1.9 保存并执行程序 ... 10
1.9.1 从命令提示符运行 Python 脚本 ... 12
1.9.2 让脚本像普通程序一样 ... 12
1.9.3 注释 ... 13
1.10 字符串 ... 13
1.10.1 单引号字符串以及对引号 转义 ... 14
1.10.2 拼接字符串 ... 15
1.10.3 字符串表示str和repr ... 15
1.10.4 长字符串、原始字符串和 字节 ... 16
1.11 小结 ... 21
1.11.1 本章介绍的新函数 ... 21
1.11.2 预告 ... 22
第 2 章 列表和元组
... 232.1 序列概述 ... 23
2.2 通用的序列操作 ... 24
2.2.1 索引... 24
2.2.2 切片... 26
2.2.3 序列相加 ... 28
2.2.4 乘法... 28
2.2.5 成员资格 ... 29
2.3 列表:Python 的主力 ... 31
2.3.1 函数list ... 31
2.3.2 基本的列表操作 ... 31
2.3.3 列表方法 ... 33
2.4 元组:不可修改的序列 ... 38
2.5 小结 ... 39
2.5.1 本章介绍的新函数 ... 40
2.5.2 预告... 40
第 3 章 使用字符串
... 413.1 字符串基本操作 ... 41
3.2 设置字符串的格式:精简版 ... 41
3.3 设置字符串的格式:完整版 ... 43
3.3.1 替换字段名 ... 43
3.3.2 基本转换 ... 44
3.3.3 宽度、精度和千位分隔符 ... 45
3.3.4 符号、对齐和用 0 填充 ... 46
3.4 字符串方法 ... 48
3.4.1 center ... 48
3.4.2 find ... 48
3.4.3 join ... 49
3.4.4 lower ... 50
3.4.5 replace ... 51
3.4.6 split ... 51
3.4.7 strip ... 51
3.4.8 translate ... 52
3.4.9 判断字符串是否满足特定的
条件 ... 52
3.5 小结 ... 53
3.5.1 本章介绍的新函数 ... 53
3.5.2 预告 ... 53
第 4 章 当索引行不通时
... 544.1 字典的用途 ... 54
4.2 创建和使用字典 ... 55
4.2.1 函数dict ... 55
4.2.2 基本的字典操作 ... 55
4.2.3 将字符串格式设置功能用于 字典 ... 57
4.2.4 字典方法 ... 58
4.3 小结 ... 63
4.3.1 本章介绍的新函数 ... 63
4.3.2 预告 ... 63
第 5 章 条件、循环及其他语句
... 645.1 再谈print和import ... 64
5.1.1 打印多个参数 ... 64
5.1.2 导入时重命名 ... 65
5.2 赋值魔法 ... 66
5.2.1 序列解包 ... 66
5.2.2 链式赋值 ... 67
5.2.3 增强赋值 ... 67
5.3 代码块:缩进的乐趣 ... 68
5.4 条件和条件语句 ... 69
5.4.1 这正是布尔值的用武之地 ... 69
5.4.2 有条件地执行和if语句 ... 70
5.4.3 else子句 ... 70
5.4.4 elif子句 ... 71
5.4.5 代码块嵌套 ... 71
5.4.6 更复杂的条件 ... 71
5.4.7 断言 ... 75
5.5 循环 ... 76
5.5.1 while循环 ... 77
5.5.2 for循环 ... 77
5.5.3 迭代字典 ... 78
5.5.4 一些迭代工具 ... 78
5.5.5 跳出循环 ... 80
5.5.6 循环中的else子句 ... 82
5.6 简单推导 ... 83
5.7 三人行 ... 84
5.7.1 什么都不做 ... 84
5.7.2 使用del删除... 85
5.7.3 使用exec和eval执行字符串 及计算其结果 ... 86
5.8 小结 ... 88
5.8.1 本章介绍的新函数 ... 89
5.8.2 预告 ... 89
第 6 章 抽象
... 906.1 懒惰是一种美德 ... 90
6.2 抽象和结构 ... 91
6.3 自定义函数 ... 91
6.3.1 给函数编写文档 ... 92
6.3.2 其实并不是函数的函数 ... 93
6.4 参数魔法 ... 93
6.4.1 值从哪里来 ... 94
6.4.2 我能修改参数吗 ... 94
6.4.3 关键字参数和默认值 ... 98
6.4.4 收集参数 ... 100
6.4.5 分配参数 ... 102
6.4.6 练习使用参数 ... 103
6.5 作用域 ... 105
6.6 递归 ... 107
6.6.1 两个经典案例:阶乘和幂 ... 108
6.6.2 另一个经典案例:二分查找 ... 109
6.7 小结 ... 112
6.7.1 本章介绍的新函数 ... 113
6.7.2 预告 ... 113
第 7 章 再谈抽象
... 1147.1 对象魔法 ... 114
7.1.1 多态 ... 115
7.1.2 多态和方法 ... 116
7.1.3 封装 ... 118
7.1.4 继承 ... 119
7.2 类 ... 119
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
18 17
7.2.1 类到底是什么 ... 119
7.2.2 创建自定义类 ... 120
7.2.3 属性、函数和方法 ... 121
7.2.4 再谈隐藏 ... 122
7.2.5 类的命名空间 ... 123
7.2.6 指定超类 ... 124
7.2.7 深入探讨继承 ... 125
7.2.8 多个超类 ... 126
7.2.9 接口和内省 ... 127
7.2.10 抽象基类 ... 128
7.3 关于面向对象设计的一些思考 ... 130
7.4 小结 ... 130
7.4.1 本章介绍的新函数 ... 131
7.4.2 预告 ... 131
第 8 章 异常
... 1328.1 异常是什么 ... 132
8.2 让事情沿你指定的轨道出错 ... 132
8.2.1 raise语句 ... 132
8.2.2 自定义的异常类 ... 133
8.3 捕获异常 ... 134
8.3.1 不用提供参数 ... 134
8.3.2 多个except子句 ... 136
8.3.3 一箭双雕 ... 136
8.3.4 捕获对象 ... 137
8.3.5 一网打尽 ... 137
8.3.6 万事大吉时 ... 138
8.3.7 最后 ... 139
8.4 异常和函数 ... 140
8.5 异常之禅 ... 140
8.6 不那么异常的情况 ... 142
8.7 小结 ... 143
8.7.1 本章介绍的新函数 ... 143
8.7.2 预告 ... 143
第 9 章 魔法方法、特性和迭代器
... 1449.1 如果你使用的不是 Python 3 ... 144
9.2 构造函数 ... 145
9.2.1 重写普通方法和特殊的构造 函数 ... 146
9.2.2 调用未关联的超类构造函数 ... 147
9.2.3 使用函数super ... 148
9.3 元素访问 ... 149
9.3.1 基本的序列和映射协议 ... 150
9.3.2 从list、dict和str派生 ... 152
9.4 其他魔法方法 ... 153
9.5 特性 ... 153
9.5.1 函数property... 154
9.5.2 静态方法和类方法 ... 155
9.5.3 __getattr__、__setattr__等 方法 ... 156
9.6 迭代器 ... 157
9.6.1 迭代器协议 ... 157
9.6.2 从迭代器创建序列 ... 159
9.7 生成器 ... 159
9.7.1 创建生成器 ... 159
9.7.2 递归式生成器 ... 161
9.7.3 通用生成器 ... 162
9.7.4 生成器的方法 ... 162
9.7.5 模拟生成器 ... 163
9.8 八皇后问题 ... 164
9.8.1 生成器的回溯 ... 164
9.8.2 问题... 165
9.8.3 状态表示 ... 166
9.8.4 检测冲突 ... 166
9.8.5 基线条件 ... 166
9.8.6 递归条件 ... 167
9.8.7 扫尾工作 ... 169
9.9 小结 ... 170
9.9.1 本章介绍的新函数 ... 170
9.9.2 预告... 171
第 10 章 开箱即用
... 17210.1 模块 ... 172
10.1.1 模块就是程序 ... 172
10.1.2 模块是用来下定义的 ... 174
10.1.3 让模块可用 ... 176
10.1.4 包 ... 177
10.2 探索模块 ... 178
10.2.1 模块包含什么 ... 178
10.2.2 使用help获取帮助 ... 179
10.2.3 文档 ... 180
10.2.4 使用源代码 ... 181
10.3 标准库:一些深受欢迎的模块 ... 181
10.3.1 sys ... 181
10.3.2 os ... 183
10.3.3 fileinput ... 185
10.3.4 集合、堆和双端队列 ... 186
10.3.5 time ... 190
10.3.6 random ... 192
10.3.7 shelve和json ... 195
10.3.8 re ... 198
10.3.9 其他有趣的标准模块 ... 210
10.4 小结 ... 211
10.4.1 本章介绍的新函数 ... 212
10.4.2 预告 ... 212
第 11 章 文件
... 21311.1 打开文件 ... 213
11.2 文件的基本方法 ... 214
11.2.1 读取和写入 ... 215
11.2.2 使用管道重定向输出 ... 215
11.2.3 读取和写入行 ... 217
11.2.4 关闭文件 ... 217
11.2.5 使用文件的基本方法 ... 218
11.3 迭代文件内容 ... 220
11.3.1 每次一个字符(或字节) ... 220
11.3.2 每次一行 ... 221
11.3.3 读取所有内容 ... 221
11.3.4 使用fileinput实现延迟行 迭代 ... 221
11.3.5 文件迭代器 ... 222
11.4 小结 ... 223
11.4.1 本章介绍的新函数 ... 223
11.4.2 预告 ... 224
第 12 章 图形用户界面
... 22512.1 创建 GUI 示例应用程序 ... 225
12.1.1 初探 ... 226
12.1.2 布局 ... 227
12.1.3 事件处理 ... 228
12.1.4 最终的程序 ... 229
12.2 使用其他 GUI 工具包 ... 230
12.3 小结 ... 231
第 13 章 数据库支持
... 23213.1 Python 数据库 API ... 232
13.1.1 全局变量 ... 233
13.1.2 异常 ... 234
13.1.3 连接和游标 ... 234
13.1.4 类型 ... 236
13.2 SQLite 和 PySQLite ... 236
13.2.1 起步 ... 237
13.2.2 数据库应用程序示例 ... 238
13.3 小结 ... 241
13.3.1 本章介绍的新函数 ... 241
13.3.2 预告 ... 241
第 14 章 网络编程
... 24214.1 几个网络模块 ... 242
14.1.1 模块socket ... 242
14.1.2 模块urllib和urllib2 ... 244
14.1.3 其他模块 ... 246
14.2 SocketServer及相关的类 ... 247
14.3 多个连接 ... 247
14.3.1 使用SocketServer实现 分叉和线程化 ... 249
14.3.2 使用select和poll实现 异步 I/O ... 249
14.4 Twisted ... 252
14.4.1 下载并安装 Twisted ... 252
14.4.2 编写 Twisted 服务器 ... 253
14.5 小结 ... 254
14.5.1 本章介绍的新函数 ... 255
14.5.2 预告 ... 255
第 15 章 Python 和 Web
... 25615.1 屏幕抓取 ... 256
15.1.1 Tidy 和 XHTML 解析 ... 257
15.1.2 Beautiful Soup ... 261
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
18 17
15.2 使用 CGI 创建动态网页 ... 262
15.2.1 第一步:准备 Web 服务器 .... 262
15.2.2 第二步:添加!#行 ... 263
15.2.3 第三步:设置文件权限 ... 264
15.2.4 CGI 安全风险 ... 264
15.2.5 简单的 CGI 脚本 ... 264
15.2.6 使用cgitb进行调试 ... 265
15.2.7 使用模块cgi ... 266
15.2.8 简单的表单 ... 267
15.3 使用 Web 框架 ... 269
15.4 Web 服务:更高级的抓取 ... 270
15.4.1 RSS 和相关内容 ... 270
15.4.2 使用 XML-RPC 进行远程 过程调用... 271
15.4.3 SOAP ... 272
15.5 小结 ... 272
15.5.1 本章介绍的新函数 ... 272
15.5.2 预告 ... 272
第 16 章 测试基础
... 27316.1 先测试再编码 ... 273
16.1.1 准确的需求说明 ... 273
16.1.2 做好应对变化的准备 ... 275
16.1.3 测试四步曲 ... 275
16.2 测试工具 ... 276
16.2.1 doctest ... 276
16.2.2 unittest... 278
16.3 超越单元测试 ... 280
16.3.1 使用 PyChecker 和 PyLint 检查源代码 ... 280
16.3.2 性能分析 ... 283
16.4 小结 ... 284
16.4.1 本章介绍的新函数 ... 285
16.4.2 预告 ... 285
第 17 章 扩展 Python
... 28617.1 鱼和熊掌兼得 ... 286
17.2 简单易行的方式:Jython 和 IronPython ... 287
17.3 编写 C 语言扩展 ... 289
17.3.1 SWIG ... 291
17.3.2 手工编写扩展 ... 294
17.4 小结 ... 298
17.4.1 本章介绍的新函数 ... 299
17.4.2 预告 ... 299
第 17 章 程序打包
... 30018.1 Setuptools 基础 ... 300
18.2 打包 ... 302
18.3 编译扩展 ... 303
18.4 使用py2exe创建可执行程序 ... 304
18.5 小结 ... 304
18.5.1 本章介绍的新函数 ... 305
18.5.2 预告 ... 305
第 19 章 趣味编程
... 30619.1 为何要有趣 ... 306
19.2 编程柔术 ... 306
19.3 原型设计 ... 307
19.4 配置 ... 308
19.4.1 提取常量 ... 308
19.4.2 配置文件 ... 309
19.5 日志 ... 311
19.6 如果你已不胜其烦 ... 312
19.7 如果你想深入学习 ... 312
19.8 小结 ... 313
第 20 章 项目 1:自动添加标签
... 31520.1 问题描述 ... 315
20.2 有用的工具 ... 316
20.3 准备工作 ... 316
20.4 初次实现 ... 317
20.4.1 找出文本块 ... 317
20.4.2 添加一些标记 ... 318
20.5 再次实现 ... 319
20.5.1 处理程序 ... 320
20.5.2 处理程序的超类 ... 320
20.5.3 规则 ... 322
20.5.4 规则的超类 ... 323
20.5.5 过滤器 ... 323
20.5.6 解析器 ... 323
20.5.7 创建规则和过滤器 ... 324
20.5.8 整合起来 ... 327
20.6 进一步探索 ... 331
第 21 章 项目 2:绘制图表
... 33221.1 问题描述 ... 332
21.2 有用的工具 ... 332
21.3 准备工作 ... 333
21.4 初次实现 ... 333
21.4.1 使用 ReportLab 绘图 ... 334
21.4.2 绘制折线 ... 335
21.4.3 编写原型 ... 335
21.5 再次实现 ... 337
21.5.1 获取数据 ... 337
21.5.2 使用LinePlot类 ... 337
21.6 进一步探索 ... 339
第 22 章 项目 3:万能的 XML
... 34022.1 问题描述 ... 340
22.2 有用的工具 ... 341
22.3 准备工作 ... 341
22.4 初次实现 ... 343
22.4.1 创建简单的内容处理程序 ... 343
22.4.2 创建 HTML 页面 ... 345
22.5 再次实现 ... 347
22.5.1 分派器混合类 ... 347
22.5.2 将首部和尾部写入文件的方 法以及默认处理程序 ... 349
22.5.3 支持目录 ... 349
22.5.4 事件处理程序 ... 350
22.6 进一步探索 ... 352
第 23 章 项目 4:新闻汇总
... 35323.1 问题描述 ... 353
23.2 有用的工具 ... 354
23.3 准备工作 ... 354
23.4 初次实现 ... 354
23.5 再次实现 ... 356
23.6 进一步探索 ... 362
第 24 章 项目 5:虚拟茶话会
... 36424.1 问题描述 ... 364
24.2 有用的工具... 364
24.3 准备工作 ... 365
24.4 初次实现 ... 366
24.4.1 ChatServer类 ... 366
24.4.2 ChatSession类 ... 367
24.4.3 整合起来 ... 369
24.5 再次实现 ... 371
24.5.1 基本的命令解释功能 ... 371
24.5.2 聊天室 ... 372
24.5.3 登录和退出聊天室 ... 372
24.5.4 主聊天室 ... 373
24.5.5 新的服务器 ... 373
24.6 进一步探索... 378
第 25 章 项目 6:使用 CGI 进行远程 编辑
... 37925.1 问题描述 ... 379
25.2 有用的工具... 379
25.3 准备工作 ... 380
25.4 初次实现 ... 380
25.5 再次实现 ... 381
25.5.1 创建文件名表单 ... 381
25.5.2 编写编辑器脚本 ... 382
25.5.3 编写保存脚本 ... 383
25.5.4 运行编辑器 ... 384
25.6 进一步探索... 386
第 26 章 项目 7:自建公告板
... 38726.1 问题描述 ... 387
26.2 有用的工具... 387
26.3 准备工作 ... 388
26.4 初次实现 ... 389
26.5 再次实现 ... 393
26.5.1 编写脚本 main.cgi ... 394
26.5.2 编写脚本 view.cgi ... 395
26.5.3 编写脚本 edit.cgi ... 396
26.5.4 编写脚本 save.cgi ... 398
26.5.5 尝试使用 ... 399
26.6 进一步探索... 400
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
18 17
第 27 章 项目 8:使用 XML-RPC
共享文件
... 40127.1 问题描述 ... 401
27.2 有用的工具 ... 402
27.3 准备工作 ... 403
27.4 初次实现 ... 403
27.4.1 实现简单的节点 ... 403
27.4.2 尝试使用 ... 408
27.5 再次实现 ... 409
27.5.1 创建客户端界面 ... 410
27.5.2 引发异常 ... 410
27.5.3 验证文件名 ... 411
27.5.4 尝试使用 ... 415
27.6 进一步探索 ... 415
第 28 章 项目 9:使用 GUI 共享文件
... 41728.1 问题描述 ... 417
28.2 有用的工具 ... 417
28.3 准备工作 ... 417
28.4 初次实现 ... 417
28.5 再次实现 ... 419
28.6 进一步探索 ... 422
第 29 章 项目 10:自制街机游戏
... 42329.1 问题描述 ... 423
29.2 有用的工具 ... 424
29.2.1 pygame ... 424
29.2.2 pygame.locals ... 424
29.2.3 pygame.display ... 424
29.2.4 pygame.font ... 425
29.2.5 pygame.sprite ... 425
29.2.6 pygame.mouse ... 425
29.2.7 pygame.event ... 425
29.2.8 pygame.image ... 426
29.3 准备工作 ... 426
29.4 初次实现 ... 426
29.5 再次实现 ... 429
29.6 进一步探索 ... 438
附录 A 简明教程
... 440附录 B Python 参考手册
... 447
1 2 3 4 5
15 6 7 8 9 10 11 12 13 14
快速上手:基础知识
该动手实践了。在本章中,你将学习如何借助计算机能够听懂的语言——Python——来控制 它。这里没有什么太难的内容,只要了解计算机的基本工作原理,就能按部就班地完成本章的示 例。我将从最简单的内容着手介绍一些基本知识,但鉴于Python功能强大,你很快就能完成一些 非常复杂的任务。
首先,需要安装Python或核实已经安装了它。如果你使用的是macOS或Linux/UNIX,请打开 终端(在Mac中为应用程序Terminal),输入python并按回车键。你将看到一条欢迎消息,其末尾 为如下提示符:
>>>
如果情况确实如此,就可以输入Python命令了,但需要注意的是,你的系统安装的可能是较 旧的Python版本。如果第一行消息的开头为Python 2,而不是Python 3,你可能要安装较新的版本,
因为Python 3在多个方面发生了翻天覆地的变化。
具体的安装步骤视使用的操作系统和安装方式而异,但最简单的方法是访问www.python.org,
其中有下载页面的链接。安装过程非常简单,不管你使用的是Windows、macOS、Linux/UNIX 还是其他操作系统,只需单击链接就可访问相应的最新版本。如果你使用的是Windows或Mac,
将下载一个安装程序,可通过运行它来安装Python。如果你使用的是Linux/UNIX,将下载到 源代码压缩文件,需要按说明进行编译,但通过使用Homebrew、APT等包管理器,可简化安装 过程。
安装Python后,尝试启动交互式解释器。要从命令行启动Python,只需执行命令
python。如 果同时安装了较旧的版本,可能需要执行命令
python3。如果你更喜欢使用图形用户界面,可启 动Python自带的应用程序IDLE。
1.1 交互式解释器
启动Python后,可看到类似于下面的提示符:
Python 3.5.0 (default, Dec 5 2015, 15:03:35)
[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.1.76)] on darwin Type "help", "copyright", "credits" or "license" for more information.
>>>
第 1 章
解释器的外观及其显示的错误消息因版本而异。虽然看上去没多大意思,但请相信我,这其 实很有趣,因为这是进入黑客殿堂的大门——对计算机进行控制的第一步。更准确地说,这是一 个交互式Python解释器。请尝试像下面这样做,以核实它是否管用:
>>> print("Hello, world!")
等你按下回车键后,将出现如下输出:
Hello, world!
>>>
如果你熟悉其他计算机语言,可能习惯了在每行末尾都加上分号。在Python中无需这样做,
因为在Python中,一行就是一行。如果你愿意,也可加上分号,但不会有任何影响(除非后面还 有其他代码),况且大家通常都不这样做。
这是怎么回事呢?
>>>是提示符,可在它后面输入一些内容。例如,如果你输入
print("Hello, world!")并按回车键,Python解释器将打印字符串
"Hello, world!",然后再次显示提示符。
如果输入截然不同的内容呢?请尝试这样做:
>>> The Spanish Inquisition SyntaxError: invalid syntax
>>>
显然,解释器没有看懂
①(如果你运行的不是IDLE,而是Linux命令行解释器,错误消息可能 稍有不同)。解释器还指出了问题出在什么地方:使用红色背景色(在命令行解释器中,使用的 是脱字符号
^)突出单词Spanish。
如果你喜欢这个解释器,可再尝试几次(要获取使用指南,可在提示符下输入命令
help()并 按回车键。在IDLE中,还可按F1来获取帮助信息),否则请接着往下读。毕竟,在不知道如何与 之交流的情况下,这个解释器并不是很有趣。
1.2 算法是什么
真刀真枪地编写程序前,先来说说何为计算机编程。简而言之,计算机编程就是告诉计算机 如何做。计算机多才多艺,但不太善于独立思考,我们必须提供详尽的细节,使用它们能够明白 的语言将算法提供给它们。算法只不过是流程或菜谱的时髦说法,详尽地描述了如何完成某项任 务。请看下面的菜谱:
鸡蛋火腿肠:先取一些火腿肠。
再加些火腿肠和鸡蛋。
如果喜欢吃辣,加些辣味火腿肠。
煮熟为止。记得每隔10分钟检查一次。
这个菜谱并不神奇,但其结构很有启发性。它由一系列必须按顺序执行的操作说明组成,其
——————————
① 毕竟,谁都没想到遇上了西班牙宗教裁判所(Spanish Inquisition)。
1 2 3 4 5
15 6 7 8 9 10 11 12 13 14
中有些可直接完成(取些火腿肠),有些需要特别注意(如果喜欢吃辣),还有一些需要重复多次
(每隔10分钟检查一次)。
菜谱和算法都由原料(对象)和操作说明(语句)组成。在这个示例中,火腿肠和鸡蛋是原 料,而操作说明包括添加火腿肠、烹饪指定的时间等。下面首先介绍一些非常简单的Python原料,
看看可以对它们做些什么。
1.3 数和表达式
交互式Python解释器可用作功能强大的计算器。请尝试执行如下操作:
>>> 2 + 2
结果应该为4,这不难。下面的运算呢?
>>> 53672 + 235253 288925
还是觉得没什么?不可否认,这是很常见的运算。(下面假设你对如何使用计算器很熟悉,知道
1 + 2 * 3
和
(1 + 2) * 3有何不同。)所有常见算术运算符的工作原理都与你预期的一致。除法
运算的结果为小数,即浮点数(float或floating-point number)。
>>> 1 / 2 0.5
>>> 1 / 1 1.0
如果你想丢弃小数部分,即执行整除运算,可使用双斜杠。
>>> 1 // 2 0
>>> 1 // 1 1
>>> 5.0 // 2.4 2.0
在较旧的Python版本中,对整数执行常规除法运算的结果与使用双斜杠类似。如果你使用的 是Python 2.x,要对整数执行常规除法运算,可在程序开头添加如下语句(稍后介绍如何编写完 整的程序),也可直接在交互式解释器中执行这条语句:
>>> from __future__ import division
注意 在上述语句中,
future前后分别是两条下划线:
__future__。
另外,从命令行运行较旧的Python版本时,还可使用命令行开关
-Qnew。1.8.2节将更详尽地 介绍
__future__。
至此,你了解了基本的算术运算符(加法、减法、乘法和除法),但还有一种与整除关系紧 密的运算没有介绍。
>>> 1 2% 1
这是求余(求模)运算符。
x % y的结果为
x除以
y的余数。换而言之,结果为执行整除时余 下的部分,即
x % y等价于
x - ((x // y) * y)。
>>> 10 // 3 3
>>> 10 3% 1
>>> 9 // 3 3
>>> 9 3% 0
>>> 2.75 0.5% 0.25
在这里,
10 // 3为
3,因为结果向下圆整,而3 × 3为9,因此余数为1。将9除以3时,结果正
好为3,没有向下圆整,因此余数为0。在需要执行之前菜谱指定的“每10分钟检查一次”之类的 操作时,这种运算可能很有用:只需检查
minute % 10是否为
0。(有关如何执行这种检查,请参 阅本章后面的旁注“先睹为快:
if语句”。)从最后一个示例可知,求余运算符也可用于浮点数。
这种运算符甚至可用于负数,但可能不那么好理解。
>>> 10 3% 1
>>> 10 % -3 -2
>>> -10 3% 2
>>> -10 % -3 -1
你也许不能通过这些示例一眼看出求余运算的工作原理,但通过研究与之配套的整除运算可 帮助理解。
>>> 10 // 3 3
>>> 10 // -3 -4
>>> -10 // 3 -4
>>> -10 // -3 3
基于除法运算的工作原理,很容易理解最终的余数是多少。对于整除运算,需要明白的一个 重点是它向下圆整结果。因此在结果为负数的情况下,圆整后将离0更远。这意味着对于
-10 // 3, 将向下圆整到
-4,而不是向上圆整到
-3。
这里要介绍的最后一个运算符是乘方(求幂)运算符。
>>> 2 ** 3 8
>>> -3 ** 2
1 2 3 4 5
15 6 7 8 9 10 11 12 13 14
-9
>>> (-3) ** 2 9
请注意,乘方运算符的优先级比求负(单目减)高,因此
-3**2等价于
-(3**2)。如果你要计 算的是
(-3)**2,必须明确指出。
十六进制、八进制和二进制
结束本节前需要指出的是,十六进制数、八进制数和二进制数分别以下面的方式表示:
>>> 0xAF 175
>>> 010 8
>>> 0b1011010010 722
这些表示法都以0打头。(如果你不明白这些表示法有何意义,说明你使用它们的机会不多,
只需将其牢记在心即可。)
1.4 变量
另一个你可能熟悉的概念是变量(variable)。如果代数对你来说不过是遥远的记忆,也不用 担心,因为Python中的变量理解起来很容易。变量是表示(或指向)特定值的名称。例如,你可 能想使用名称
x来表示
3,为此执行如下代码:
>>> x = 3
这称为赋值(assignment),我们将值
3赋给了变量
x。换而言之,就是将变量
x与值(或对象)
3
关联起来。给变量赋值后,就可在表达式中使用它。
>>> x * 2 6
不同于其他一些语言,使用Python变量前必须给它赋值,因为Python变量没有默认值。
注意 在Python中,名称(标识符)只能由字母、数字和下划线(_)构成,且不能以数字打头。
因此
Plan9是合法的变量名,而
9Plan不是
①。
1.5 语句
前面使用的几乎都是表达式,相当于菜谱中的原料,但语句(菜谱中的操作说明)是什么样的呢?
——————————
① 在某种程度上说,标识符命名规则基于Unicode标准,详情请参阅“Python语言参考手册”(https://docs.python.org/
3/reference/lexical_analysis.html)。
实际上,刚才说的不完全正确,因为前面已经介绍过两种语句了:
print语句和赋值语句。
语句和表达式有何不同呢?你可以这样想:表达式是一些东西,而语句做一些事情。例如,
2 * 2的结果是
4,而
print(2 * 2)打印
4。表达式和语句的行为很像,因此它们之间的界线可能并非那
么明确。
>>> 2 * 2 4
>>> print(2 * 2) 4
在交互式解释器中执行时,这两段代码的结果没有任何差别,但这是因为解释器总是将表达 式的值打印出来(打印的是
repr表示的内容,详情请参阅1.10.3节)。然而,在Python中,情况并 非都是这样的。本章后面将介绍如何创建无需交互式解释器就能运行的程序。仅将诸如
2 * 2等 表达式放在程序中不会有任何作用
①,但在程序中包含
print(2 * 2)将打印结果
4。
注意
print实际上是一个函数(这将在本章后面更详细地介绍),因此前面说的
print语句其实
是函数调用。在Python 2.x中,
print是一条语句,无需将要打印的内容作为参数放在圆括 号内。
涉及赋值时,语句和表达式的差别更明显:鉴于赋值语句不是表达式,它们没有可供交互式 解释器打印的值。
>>> x = 3
>>>
执行赋值语句后,交互式解释器只是再次显示提示符,但发生了一些变化:有一个名为
x的 新变量,与值
3相关联。可以说,这是所有语句的一个根本特征:执行修改操作。例如,赋值语 句改变变量,而
print语句改变屏幕的外观。
无论在什么编程语言中,赋值语句都可能是最重要的语句,虽然这一点你可能难以马上明白。
变量就像是临时“存储区” (类似于菜谱中的锅碗瓢盆)
②,其真正威力在于无需知道它们存储的 值就能操作它们。
例如,即便根本不知道
x和
y是什么,你也知道
x * y的结果为
x和
y的乘积。因此,编写程序 时,你能以各种方式使用变量,而无需知道程序运行时它们将存储(指向)的值。
——————————
① 这个表达式确实会执行一些操作:计算2和2的乘积。但既不会将结果保存起来,也不会向用户显示它。因此除执 行计算外,没有其他任何作用。
② 请注意,这里给“存储区”加上了引号。值并非存储在变量中,而是存储在变量指向的计算机内存中。多个变量 可指向同一个值。深入阅读后会更清楚地了解这一点。
1 2 3 4 5
15 6 7 8 9 10 11 12 13 14 1.6 获取用户输入
前面说过,编写程序时无需知道变量的值就可使用它们。当然,解释器最终必须知道变量的 值,可它怎么知道我们不知道的事情呢?解释器只知道我们已告知它的内容,不是吗?未必如此。
你编写的程序可能供他人使用,无法预测用户会向程序提供什么样的值。我们来看看很有用
的函数
input(稍后将更详细地介绍函数)。
>>> input("The meaning of life: ") The meaning of life: 42
'42'
这里在交互式解释器中执行了第一行(
input(...)),它打印字符串
"The meaning of life:", 提示用户输入相应的信息。我输入
42并按回车。这个数被
input(以文本或字符串的方式)返回,
并在最后一行被自动打印出来。通过使用
int将字符串转换为整数,可编写一个更有趣的示例:
>>> x = input("x: ") x: 34
>>> y = input("y: ") y: 42
>>> print(int(x) * int(y)) 1428
对于上述在Python提示符(
>>>)下输入的语句,可将其放在完整的程序中,并让用户提供 所需的值(
34和
42)。这样,这个程序将打印结果
1428,即前述两个数的乘积。在这种情况下,
你编写程序时无需知道这些值,对吧?
注意 将程序存储在独立的文件中,让其他用户能够执行时,这种获取输入的方式将有用得多。
1.8节将介绍如何这样做。
先睹为快:if语句
为增添学习乐趣,这里提前说说原本要到第5章才介绍的内容:
if语句。通过使用
if语 句,可在给定条件满足时执行特定的操作(另一条语句)。一种条件是使用相等运算符(
==) 表示的相等性检查。没错,相等运算符就是两个等号。(一个等号用于赋值,还记得吗?)
你将条件放在
if后面,再加上冒号,将其与后面的语句分开。
>>> if 1 == 2: print('One equals two') ...
>>> if 1 == 1: print('One equals one') ...
One equals one
>>>
条件不满足时什么都不做,但条件满足时,将执行冒号后面的语句(这里是一条
print语 句)。需要注意的另一点是,在交互式解释器中输入
if语句后,需要按两次回车键才能执行它
(其中的原因将在第5章介绍)。
因此,如果变量
time指向的是以分钟为单位的当前时间,可使用如下语句检查当前是不 是整点:
if time 60 == 0: print('On the hour!')%
1.7 函数
1.3节使用了乘方运算符(
**)来执行幂运算。实际上,可不使用这个运算符,而使用函数
pow。
>>> 2 ** 3 8
>>> pow(2, 3) 8
函数犹如小型程序,可用来执行特定的操作。Python提供了很多函数,可用来完成很多神奇 的任务。实际上,你也可以自己编写函数(这将在后面更详细地介绍),因此我们通常将
pow等标 准函数称为内置函数。
像前一个示例那样使用函数称为调用函数:你向它提供实参(这里是
2和
3),而它返回一个 值。鉴于函数调用返回一个值,因此它们也是表达式,就像本章前面讨论的算术表达式一样
①。 实际上,你可结合使用函数调用和运算符来编写更复杂的表达式(就像前面使用函数
int时那样)。
>>> 10 + pow(2, 3 * 5) / 3.0 10932.666666666666
有多个内置函数可用于编写数值表达式。例如,
abs计算绝对值,
round将浮点数圆整为与之 最接近的整数。
>>> abs(-10) 10
>>> 2 // 3 0
>>> round(2 / 3) 1.0
请注意最后两个表达式的差别。整数总是向下圆整,而
round圆整到最接近的整数,并在两 个整数一样近时圆整到偶数。如果要将给定的数向下圆整,该如何做呢?例如,你知道某人的年 龄为32.9,并想将这个值向下圆整为32,因为他还没有满33岁。Python提供了完成这种任务的函
数
floor,但你不能直接使用它,因为像众多很有用的函数一样,它也包含在模块中。
1.8 模块
可将模块视为扩展,通过将其导入可以扩展Python功能。要导入模块,可使用特殊命令
——————————
① 函数调用也可用作语句,但在这种情况下,将忽略函数的返回值。
1 2 3 4 5
15 6 7 8 9 10 11 12 13 14
import
。前一节提及的函数
floor包含在模块
math中。
>>> import math
>>> math.floor(32.9) 32
请注意其中的工作原理:我们使用
import导入模块,再以
module.function的方式使用模块 中的函数。就这里执行的操作而言,也可像前面处理
input的返回值那样,将这个数字转换为 整数。
>>> int(32.9) 32
注意 还有一些类似的函数,可用于转换类型,如
str和
float。实际上,它们并不是函数,而 是类。类将在本书后面更详细地介绍。
模块
math还包含其他几个很有用的函数。例如,
ceil与
floor相反,返回大于或等于给定数 的最小整数。
>>> math.ceil(32.3) 33
>>> math.ceil(32) 32
如果确定不会从不同模块导入多个同名函数,你可能不想每次调用函数时都指定模块名。在 这种情况下,可使用命令
import的如下变种:
>>> from math import sqrt
>>> sqrt(9) 3.0
通过使用命令
import的变种
from module import function,可在调用函数时不指定模块前缀。
提示 事实上,可使用变量来引用函数(以及其他大部分Python元素)。执行赋值语句
foo = math.sqrt后,就可使用
foo来计算平方根。例如,
foo(4)的结果为
2.0。
1.8.1 cmath 和复数
函数
sqrt用于计算平方根。下面来看看向它提供一个负数的情况:
>>> from math import sqrt
>>> sqrt(-1)
Traceback (most recent call last):
...
ValueError: math domain error
在有些平台上,结果如下:
>>> sqrt(-1) nan
注意
nan具有特殊含义,指的是“非数值”(not a number)。
如果我们坚持将值域限定为实数,并使用其近似的浮点数实现,就无法计算负数的平方根。
负数的平方根为虚数,而由实部和虚部组成的数为复数。Python标准库提供了一个专门用于处理 复数的模块。
>>> import cmath
>>> cmath.sqrt(-1) 1j
注意到这里没有使用
from ... import ...。如果使用了这种
import命令,将无法使用常规函 数
sqrt。类似这样的名称冲突很隐蔽,因此除非必须使用
from版的
import命令,否则应坚持使用 常规版
import命令。
1j
是个虚数,虚数都以
j(或
J)结尾。复数算术运算都基于如下定义:
-1的平方根为
1j。这 里不深入探讨这个主题,只举一个例子来结束对复数的讨论:
>>> (1 + 3j) * (9 + 4j) (-3 + 31j)
从这个示例可知,Python本身提供了对复数的支持。
注意 Python没有专门表示虚数的类型,而将虚数视为实部为零的复数。
1.8.2 回到未来
据说Python之父Guido van Rossum有一台时光机,因为这样的情况出现了多次:大家要求 Python提供某项功能时,却发现这项功能早已实现。当然,并非什么人都能进入这台时光机,不 过Guido很体贴,通过神奇模块
__future__让Python具备了时光机的部分功能。对于Python当前不 支持,但未来将成为标准组成部分的功能,你可从这个模块进行导入。这一点你在1.3节已经见 识过,本书后面也将经常遇到这个模块。
1.9 保存并执行程序
交互式解释器是Python的亮点之一,它让你能够实时地测试解决方案以及尝试使用Python。
要了解隐藏在背后的工作原理,只需尝试使用即可!然而,等你退出交互式解释器时,你在其中 编写的所有代码都将丢失。你的终极目标是编写自己和他人都能运行的程序。本节将介绍如何达 成这种目标。
首先,你需要一个文本编辑器——最好是专门用于编程的(不推荐使用Microsoft Word之类 的软件,但如果你使用的是这样的软件,务必以纯文本的方式保存代码)。如果你使用的是IDLE,
那就太幸运了。在这种情况下,只需选择菜单File→New File。这将新建一个编辑器窗口,其中
1 2 3 4 5
15 6 7 8 9 10 11 12 13 14
没有交互式提示符。首先,输入如下代码:
print("Hello, world!")
接下来,选择菜单File→Save保存程序(其实就是一个纯文本文件)。务必将文件存储在以后 能够找到的地方,并指定合理的文件名,如hello.py(扩展名.py很重要)。
保存好了吗?请不要关闭包含程序的窗口。如果关闭了,选择菜单File→Open重新打开。现 在可以运行这个程序了,方法是选择菜单Run→Run Module。(如果你使用的不是IDLE,请参阅 下一节,了解如何从命令提示符运行程序。)
结果如何呢?在解释器窗口中打印了
Hello, world!,这正是我们想要的结果。根据你使用 的版本,解释器提示符可能消失,要让它重新出现,可在解释器窗口中按回车键。
接下来,将脚本扩展成下面这样:
name = input("What is your name? ") print("Hello, " + name + "!")
如果你运行这个脚本(别忘了先保存),将在解释器窗口中看到如下提示信息:
What is your name?
输入你的名字(如Gumby)并按回车键,你将看到类似于下面的内容:
Hello, Gumby!
强大的海龟绘图法
编写简单示例时,
print语句很有用,因为几乎在任何地方都可使用它。如果你要尝试提 供更有趣的输出,应考虑使用模块
turtle,它实现了海龟绘图法。如果你正在运行IDLE,就 可使用这个模块,它让你能够绘制图形(而不是打印文本)。通常,应避免导入模块中所有的 名称,但尝试使用海龟绘图法时,这样做可提供极大的方便。
from turtle import *
确定需要使用哪些函数后,可回过头去修改
import语句,以便只导入这些函数。
海龟绘图法的理念源自形如海龟的机器人。这种机器人可前进和后退,还可向左和向右 旋转一定的角度。另外,这种机器人还携带一只铅笔,可通过抬起或放下来控制铅笔在什么 时候接触到脚下的纸张。模块
turtle让你能够模拟这样的机器人。例如,下面的代码演示了 如何绘制一个三角形:
forward(100) left(120) forward(100) left(120) forward(100)
如果你运行这些代码,将出现一个新窗口,其中有一个箭头形“海龟”不断地移动,并 在 身 后 留 下 移 动 轨 迹 。 要 将 铅 笔 抬 起 , 可 使 用
penup(); 要 将 铅 笔 重 新 放 下 , 可 使 用
pendown()
。要了解其他的命令,请参阅“Python库参考手册”的相关部分(https://docs.python.
org/3/library/turtle.html)。要了解如何绘图,可尝试在网上搜索海龟绘图法(turtle graphic)。
学习更多的概念后,你可能想用海龟绘图法替换平淡的
print语句。在尝试使用海龟绘图法的 过程中,你很快就会发现需要使用后面将介绍的一些基本编程结构。例如,如何在前面的示 例中避免反复调用命令
forward和
left,如何绘制八角形(而不是三角形)以及如何以尽可能 少的代码绘制多个边数各不相同的正多边形。
1.9.1 从命令提示符运行 Python 脚本
实际上,运行程序的方式有多种。首先,假定你打开了DOS窗口或UNIX shell,并切换到了 Python可执行文件(在Windows中为python.exe,在UNIX中为python)或将该可执行文件所在的 目录加入到了环境变量
PATH中(仅适用于Windows)
①。另外,假定前一节的脚本(hello.py)存 储在当前目录下。满足上述条件后,就可在Windows中使用如下命令来执行这个脚本:
C:\>python hello.py
在UNIX系统中,可使用如下命令:
$ python hello.py
如你所见,命令是一样的,只是系统提示符不同。
1.9.2 让脚本像普通程序一样
在有些情况下,你希望能够像执行其他程序(如Web浏览器或文本编辑器)一样执行Python 脚本,而无需显式地使用Python解释器。UNIX提供了实现这种目标的标准方式:让脚本的第一 行以字符序列
#!(称为pound bang或shebang)开始,并在它后面指定用于对脚本进行解释的程序
(这里是Python)的绝对路径。即便你对这一点不太明白,只需将下面的代码作为脚本的第一行,
就可在UNIX中轻松运行脚本:
#!/usr/bin/env python
不管Python库位于什么地方,这都将让你能够像运行普通程序一样运行脚本。如果你安装了 多个版本的Python,可用更具体的可执行文件名(如python3)替换python。
要像普通程序一样运行脚本,还必须将其变成可执行的:
$ chmod a+x hello.py
现在,可以像下面这样来运行它(假定当前目录包含在执行路径中):
$ hello.py
如果这不管用,请尝试使用./hello.py,这在当前目录(.)未包含在执行路径中时也管用(负
——————————
① 如果你看不懂这句话,可以跳过1.9.1节,因为这一节的内容不是非得掌握的。
1 2 3 4 5
15 6 7 8 9 10 11 12 13 14
责的系统管理员会告诉你执行路径是什么)。
如果你愿意,可对文件进行重命名并删除扩展名.py,使其看起来更像普通程序。
如果双击会如何呢
在Windows中,扩展名.py是让脚本像普通程序一样的关键所在。请尝试双击前一节保存的文 件hello.py。如果正确地安装了Python,这将打开一个DOS窗口,其中包含提示信息What is your name?
①。然而,这样运行程序存在一个问题:输入名字后,程序窗口将立即关闭,你根本来不及 看清结果。这是因为程序结束后窗口将立即关闭。尝试修改脚本,在末尾添加如下代码行:
input("Press <enter>")
现在运行这个程序并输入名字后,DOS窗口将包含如下内容:
What is your name? Gumby Hello, Gumby!
Press <enter>
等你按回车键后,窗口将立即关闭,因为程序结束了。
1.9.3 注释
在Python中,井号(
#)比较特殊:在代码中,井号后面到行尾的所有内容都将被忽略。(这 也是Python解释器未被前面的/usr/bin/env卡住的原因所在。)下面是一个示例:
# 打印圆的周长:
print(2 * pi * radius)
第一行为注释。注释让程序更容易理解:对其他人来说如此,在程序编写者回过头来阅读代 码时亦如此。据说程序员应遵守的首要戒律是“汝应注释”,但是一些不那么宽容的程序员的座 右铭是“如果写起来难,理解起来必然也难”。注释务必言而有物,不要重复去讲通过代码很容 易获得的信息。无用而重复的注释还不如没有。例如,下述代码中的注释根本就是多余:
# 获取用户的名字:
user_name = input("What is your name?")
在任何情况下,都应确保代码即便没有注释也易于理解。所幸Python是一种卓越的语言,能 让人很容易编写出易于理解的程序。
1.10 字符串
前一节的代码
"Hello, " + name + "!"是什么意思呢?本章的第一个程序只包含如下代码:
print("Hello, world!")
——————————
① 是否会这样取决于你使用的操作系统以及安装的Python解释器。例如,在macOS中,如果文件是使用IDLE存储的,
双击文件将只会在IDLE代码编辑器中打开它。
编程教程通常以类似的程序开篇,问题是我还未全面阐述其工作原理。你已掌握了
print语 句的基本知识(后面将更详细地介绍它),但
"Hello, world!"是什么呢?这是一个字符串(string)。
几乎所有真实的Python程序中都有字符串的身影。字符串用途众多,但主要用途是表示一段文本,
如感叹句“Hello, world!”。
1.10.1 单引号字符串以及对引号转义
与数一样,字符串也是值:
>>> "Hello, world!"
'Hello, world!'
在这个示例中,有一点可能让你颇感意外:Python在打印字符串时,用单引号将其括起,而 我们使用的是双引号。这有什么差别吗?其实没有任何差别。
>>> 'Hello, world!' 'Hello, world!'
这里使用的是单引号,结果却完全相同。既然如此,为何同时支持单引号和双引号呢?因为 在有些情况下,这可能会有用。
>>> "Let's go!"
"Let's go!"
>>> '"Hello, world!" she said' '"Hello, world!" she said'
在上述代码中,第一个字符串包含一个单引号(就这里而言,可能称之为撇号更合适),因 此不能用单引号将整个字符串括起,否则解释器将报错(做出这样的反应是正确的)。
>>> 'Let's go!'
SyntaxError: invalid syntax
在这里,字符串为
'Let',因此Python不知道如何处理后面的
s(更准确地说是当前行余下的 内容)。
第二个字符串包含双引号,因此必须使用单引号将整个字符串括起,原因和前面一样。实 际上,并非必须这样做(这样做只是出于方便考虑)。可使用反斜杠(
\)对引号进行转义,如 下所示:
>>> 'Let\'s go!'
"Let's go!"
这样Python将明白中间的引号是字符串的一部分,而不是字符串结束的标志。虽然如此,
Python打印这个字符串时,还是使用了双引号将其括起。与你预期的一样,对于双引号可采用同 样的处理手法。
>>> "\"Hello, world!\" she said"
'"Hello, world!" she said'
像这样对引号进行转义很有用,且在有些情况下必须这样做。例如,在字符串同时包含单引