描述
开 本: 16开纸 张: 胶版纸包 装: 平装-胶订是否套装: 否国际标准书号ISBN: 9787111632955
一本值得你反复研读的Python佳作,注重内功修炼,而非花拳绣腿
来自于真实教学经验的详略取舍,注重对知识的探寻和思考过程
透过Python的表象,深入程序设计的本质,让你知其然并知其所以然
用数据看本书亮点:
12小时配套教学视频
92个细化的学习目标
185幅程序解析精美插图
89段可运行的完整实例代码
100个代码说明与解析
50个实例程序运行结果
233个思考和扩展练习题
63段注意、提示类小段落
199个脚注说明
47个参考文献
本书主要特色:
采用清晰明快的行文结构
呈现Python朴素平实的编程思想
探讨Python与其他编程语言的不同
详细讲解Python的重点基础语法
深入讲解状态机和递归等核心技术
深入剖析Python程序的具体实现
给出完整可执行的实例程序
给出大多数实例程序的运行结果
各小节后给出延伸的思考和扩展练习题
给读者提供了更加开阔的思路
以真实的测试数据支撑所述观点
本书精华内容:
数据类型、流程控制、输入/输出、函数等基础知识
状态机、递归、函数式编程等高级技巧
结合Python内建类型的数据结构
面向对象的设计思想
性能分析和优化
超值配书资源:
配套教学视频
实例源代码文件
教学PPT
本书以Python语言构建了程序设计基础课程的教学体系。本书在对程序设计核心方法的探讨上较其他Python书籍更为全面和深入。通过对本书内容的系统学习,读者将全面掌握用Python进行程序设计的基本能力。
本书共分为4章。第1章介绍了Python的基本语法和程序设计的一般方法,涵盖表达式、程序运行、内建类型、赋值、引用、流程控制结构、输入/输出、核心设计方法、算法性能描述、异常处理和程序调试等内容。第2章介绍了函数这一重要概念,涵盖函数基础、模块、包、作用域、栈帧、递归、类、成员方法、高阶函数、迭代器和生成器模式等内容。第3章通过Python的内建类型讲述了常见的数据结构,涵盖列表、链表、散列表和二叉树等内容,并介绍了deque链表块和OrderedDict有序字典两个案例,*后介绍了综合练习——寻路问题算法。第4章介绍了Python的面向对象特性,涵盖类、继承和多态等重要概念,并以一个综合练习——GUI程序设计PyQt结束全书。
本书讲解由浅入深,循序渐进,适合Python编程的自学人员和爱好者阅读,也适合作为高校理工科专业的Python教学用书,还适合作为IT培训机构的Python教学用书。
前言
第1章 基础1
1.1 历史2
1.2 表达式3
1.2.1 运算数3
1.2.2 运算符3
1.2.3 表达式的风格4
1.2.4 表达式的嵌套5
1.2.5 数据类型5
1.2.6 副作用6
1.2.7 小结6
1.3 运行程序6
1.3.1 交互执行模式7
1.3.2 查阅帮助文档8
1.3.3 执行Python程序脚本9
1.3.4 标识符和关键字10
1.3.5 运行环境的错误提示11
1.3.6 示例:欧几里得算法12
1.3.7 小结15
1.4 内建类型、赋值和引用15
1.4.1 字面值15
1.4.2 构造方法17
1.4.3 容器类型18
1.4.4 索引和切片22
1.4.5 左值、赋值和引用24
1.4.6 del操作29
1.4.7 小结30
1.5 流程控制结构30
1.5.1 if分支语句30
1.5.2 布尔运算33
1.5.3 while循环34
1.5.4 for循环40
1.5.5 条件表达式42
1.5.6 定义简单函数43
1.5.7 小结44
1.6 输入/输出44
1.6.1 标准输入/输出(I/O)流44
1.6.2 重定向标准I/O至文件45
1.6.3 用管道行串接I/O46
1.6.4 标准I/O流对象47
1.6.5 命令行参数48
1.6.6 环境变量49
1.6.7 格式化字符串50
1.6.8 小结51
1.7 简单练习51
1.7.1 示例:打印金字塔图形52
1.7.2 示例:3X 1问题53
1.7.3 示例:绘制正多边形54
1.7.4 示例:绘制函数曲线55
1.7.5 示例:蒙特卡洛方法56
1.7.6 示例:埃特金迭代法求方程的根59
1.7.7 小结61
1.8 程序执行模型61
1.8.1 手段限制62
1.8.2 无状态程序62
1.8.3 有状态程序67
1.8.4 线性存储器73
1.8.5 使用栈设计程序76
1.8.6 使用队列设计程序79
1.8.7 小结84
1.9 算法的性能描述85
1.10 异常处理87
1.10.1 基本语法88
1.10.2 提升程序的健壮性91
1.10.3 完整的异常捕获机制94
1.10.4 小结96
1.11 程序调试97
1.12 总结98
第2章 函数99
2.1 函数基础100
2.1.1 函数的作用100
2.1.2 定义和调用函数101
2.1.3 提供机制而非策略102
2.1.4 用函数消除重复代码103
2.1.5 Lambda表达式105
2.1.6 回调函数106
2.1.7 闭包107
2.1.8 传参方式108
2.1.9 文档字符串110
2.1.10 小结111
2.2 模块和包111
2.2.1 处理名字冲突111
2.2.2 模块与import112
2.2.3 在模块中包含测试代码113
2.2.4 模块搜索路径113
2.2.5 包114
2.2.6 小结115
2.3 作用域和栈帧115
2.3.1 名字的查找115
2.3.2 nonlocal和global关键字118
2.3.3 函数的调用栈118
2.3.4 对象的生命期119
2.3.5 小结121
2.4 递归121
2.4.1 单重递归121
2.4.2 多重递归124
2.4.3 示例:科赫雪花125
2.4.4 示例:二叉树的后序遍历127
2.4.5 消除尾递归129
2.4.6 用栈和状态机消除递归131
2.4.7 重复递归带来的性能陷阱135
2.4.8 用动态规划消除重复递归136
2.4.9 示例:通配符匹配139
2.4.10 小结141
2.5 类和成员方法141
2.5.1 面向对象的函数调用风格142
2.5.2 类和实例143
2.5.3 定义类144
2.5.4 创建实例145
2.5.5 方法定义145
2.6 高阶函数147
2.6.1 对函数进行运算148
2.6.2 函数装饰器149
2.6.3 map和filter函数152
2.6.4 小结155
2.7 迭代器和生成器模式155
2.7.1 可迭代对象和迭代器156
2.7.2 生成器函数157
2.7.3 列表推导式和生成器表达式158
2.7.4 小结160
2.8 总结160
第3章 数据结构161
3.1 列表162
3.1.1 数组和内存162
3.1.2 列表对象的结构165
3.1.3 列表元素的插入168
3.1.4 列表的排序170
3.1.5 有序列表的二分查找171
3.1.6 列表的基本操作接口172
3.1.7 小结173
3.2 链表174
3.2.1 单链表175
3.2.2 实现迭代器模式178
3.2.3 用单链表实现栈179
3.2.4 双向循环链表180
3.2.5 用双向链表实现队列182
3.2.6 双向链表的查找、插入和删除183
3.2.7 小结184
3.3 散列表184
3.3.1 基本原理185
3.3.2 应用示例188
3.3.3 字典189
3.3.4 小结193
3.4 二叉树194
3.4.1 概念和定义194
3.4.2 表示和存储196
3.4.3 遍历198
3.4.4 二叉搜索树200
3.4.5 二叉堆和优先队列207
3.4.6 哈夫曼编码209
3.4.7 小结212
3.5 案例分析212
3.5.1 deque链表块212
3.5.2 OrderedDict有序字典216
3.6 综合练习:寻路问题算法218
3.6.1 图的表示219
3.6.2 Dijkstra算法221
3.6.3 A*算法224
3.7 总结231
第4章 面向对象232
4.1 类233
4.1.1 术语233
4.1.2 成员方法234
4.1.3 静态方法236
4.1.4 类属性和类方法237
4.1.5 私有成员239
4.1.6 property装饰器240
4.1.7 动态添加属性和slots242
4.1.8 实例的生命周期243
4.1.9 复制对象244
4.1.10 小结245
4.2 继承和多态246
4.2.1 语法246
4.2.2 如何设计类249
4.2.3 多继承251
4.2.4 鸭子类型和多态252
4.2.5 小结253
4.3 综合练习:GUI程序设计PyQt253
4.3.1 安装PyQt254
4.3.2 使用继承创建窗体255
4.3.3 响应事件256
4.3.4 小结257
4.4 总结257
参考文献258
生产力水平的发展是人类历史的主旋律。决定生产力水平的重要因素之一是生产工具。从这个角度讲,人类历史就是从石块到计算机的历史:从石器时代到信息时代。计算机是信息时代的主要生产工具,为了掌握这种工具,就要学习程序设计语言。
程序设计语言很多,相关的书籍更多。初学者常困惑于如何选择入门语言①,一旦决定后又困惑于选择什么教材。令人沮丧的是,选择的机会成本并不低:一般初学者完整地学习某门编程语言需要一个月甚至更多时间。大专院校课程改革的机会成本则更高。有人说选择什么语言入门不重要,成为高手后自然兼通各种语言。这种说法忽视了大多数学习者并不会成为高手的事实,而且选择性遗忘了他们自己作为初学者时所走的弯路。总而言之,学习者和教师在选择适合入门的编程语言和书籍时应当谨慎行事。因此笔者有必要在前言中将写作意图和内容取舍进行说明,以作为读者选择的依据。
写作背景和目的
笔者在2018年秋接到邀约并着手写作,全稿终于2019年春。此时Python已经跻身主流语言之列。之所以称其为“主流”语言,在笔者看来有以下几点证据:
? 在各类有影响力的语言排行榜上,使用Python的比例开始占据较大份额,排名也很靠前②;
? 在许多领域,Python成为主要的编程语言,甚至是首选语言,例如在数据分析、人工智能和服务器开发等领域;
? 教育部将Python纳入了国家计算机等级考试的科目③。
而在此之前其已经得到了广泛应用:
? Python逐渐成为许多国际一流高校程序设计课程的教学语言;
? 许多基础软件框架采用Python作为设计语言或提供Python的编程接口;
? Python在开源软件界和工业界已广受欢迎。
上述事实是笔者决定基于Python编写本书的原因④。其他的原因则是在教学中无合适教材可用。数年前笔者领导团队设计Python课程体系,那时曾经“遍访”各种Python书籍和教程。当时的Python书籍或者是简单的“手把手入门”,或者是大部头的“知识大全”,或者是应用于某具体领域的“手册指南”。
随着近年来Python教学实践的展开,市面上开始出现各种教科书体裁的Python书籍(如罗伯特•塞奇威克等所著的《程序设计导论:Python语言实践》)。那为何还要再写一本同样主题的教科书呢?因为教学对象不同,教学目标有所差别,教学过程各有侧重。所以教科书的首要价值在于“不同”。这种不同在本书来说至少有两个层面,一是Python与其他语言的不同,二是本书与其他Python书籍的不同。
学习或讲授新知识时,首先应注意到的往往是其与原有认知的相同之处,但唯有深入理解并能运用其不同后,才算窥得门径。例如Python也有循环、分支和函数等各种语言俱有之概念,但学习者如只见共性,并由此得出Python很简单的结论,便失去了学习新语言的意义。教师在教学生时也不能仅仅按照讲授旧有语言的经验,把讲义中的例子依次用Python重写一遍了事。①
书的“不同”则在于体裁形式、内容取舍编排和作者观点的不同。既然是教科书,总不大好写成对话体,也不适宜画成漫画②。所以教科书的不同主要体现在取舍、次序、示例和观点等方面。然而写一本“完全不一样”的书是很难的,尤其是在Python已经相当流行的当下。③
“不同”总是相对的,各种相对性参照中,对读者来说最重要的参照便是读者自身。初来者看处处皆是新奇,见多识广后则觉得不过尔尔。所以下文仅对本书内容的取舍和编排进行说明,至于其中有何“不同”则留待读者自己体会。
取舍
在教科书中全面讲授Python的细节是不现实的(这里说的语法细节包括完整的语法模型、各种对象的API④,以及各种标准库),原因有以下几点:
? 过多的细节会喧宾夺主,无法凸显真正重要的核心内容;
? 语言中的艰深部分不适合本书面向的教学阶段,也不常用于多数程序员的日常工作;
? Python标准库包罗万象,全面介绍是不现实的(无论从篇幅上还是读者的知识背景基础而言);
? Python在不断发展,其细节仍然在不断变化中。
笔者认为,既然在整体上无法面面俱到,那么在局部也不应有这种负担。例如“异常处理”这一主题,如果完整地讲授异常机制的每个细节(如各种内建异常类型),则需要相当多的篇幅①。但真实的情况是:绝大多数的工程师根本不懂如何处理程序的意外情况,不论是使用C语言这样没有异常处理机制的语言,还是使用Java或Python这样有完整异常处理机制的语言。究其原因有以下几点:
? 一是初学者没有能力接受太多的异常处理知识,正常程序还写不明白,哪里有精力去整什么异常处理;
? 二是异常处理的核心在于全面、准确地剖析程序的各种意外情况,不具备这个能力,学习再多的异常处理机制也是枉然;
? 三是在学习程序设计的初级阶段,往往用算法进行练习(比如走个迷宫、匹配个字符串),不和复杂的外部世界(如网络)打交道;
? 四是考虑到教学重点和叙述篇幅,也往往假定数据有效,这样就不用考虑处理意外情况。这样一来,教学中费大力气讲授的异常处理机制,也因为暂时无用而被抛诸脑后。
本书则不纠缠于异常处理的细节和内建异常的类型,而是在讲述完基本语法后详细剖析一个程序实例(1.10.2节),展示如何分析和处理各种意外的输入情况,篇幅上也仅限制为一节。读者若能“吃透”这一部分内容,则当有编写健壮程序的意识,自能够在工作中举一反三,也无须笔者再行赘述。反之,若对健壮性不够重视,则多费篇幅也没有意义。
作为这种取舍思路的补充,本书有时会指出一些自学内容,将其留作练习,并引导读者进行思考或通过网络查阅相关资料。作为编程入门教科书,本书首要讲述程序设计的一般性方法,例如:
? 数据类型、流程控制、输入/输出和函数等基本手段;
? 状态机、递归、函数式编程等高级技巧;
? 数据结构和算法;
? 面向对象的设计思想。
对Python自身语法的介绍,也在本书各部分占据了相当篇幅。然而本书终归不是语法手册,所以并不追求语法知识细节的完整性。书中大部分小节后会给出延伸的问题和练习,或是明确的编码练习,或是要求读者进行深入地学习和思考,统称为“思考和扩展练习”。本书在设计和挑选例题时力图做到和传统的程序设计入门书籍在角度和深度上有所差异,目的在于为学生和教师提供更加开阔的思路。
计算机程序设计的理论基石(如语言基本模型、数据结构和算法)是在半个世纪前奠定的。虽然Python诞生的年代稍晚,但也基于这些基石构建而成。Python的主体部分是基于朴素平实的想法构建而成,而非天才的灵光一现。本书的一个重要初衷就是将这些朴素呈现给读者。笔者舍去了一些和该初衷无关的内容,如传统教学中往往着重介绍的字符串显示控制标记,在本书中只是一带而过。而那些更为深层次的基本概念则得到了强化,例如状态机模型、递归消除、面向对象动机及性能权衡等。
本书在示例中坚持给出完整可执行的代码或交互界面步骤,然而本书绝非“手把手”的教程。书中有些内容对于学习者来说是具有相当难度的,需要读者反复阅读思考并且动手实践才能够理解或掌握。这种难度是具有现实意义的:较难内容大多数来自于工程实践、后续学习,甚至是求职面试中的重点。读者应当认识到对这些问题的思考能力和思考过程的重要性。
内容编排
本书分为4章,取名为:
? 第1章 基础;
? 第2章 函数;
? 第3章 数据结构;
? 第4章 面向对象。
上述各章内容并非泾渭分明,原因主要在于Python的知识点存在许多“循环依赖”,如图1所示。例如for循环用到了迭代器的概念,而讲授迭代器需要足够的面向对象知识,绕过分支控制先完整地讲述面向对象也不现实。对于有经验的读者来说,这没有任何问题,但对初学者则会造成很大困扰。基于这些原因及教学实践,本书在第1章中简要讲授了函数定义而非拖至第2章,将定义类的基本方法提前至第2章而不是留待第4章。这两节内容的调整是本书的重要关节。
图1 知识点的循环依赖
这4章内容以第1章篇幅最长,第2、3章次之,第4章最短。程序的基本设计方法、异常处理和程序调试等内容归入了第1章。模块和迭代器归入了第2章。部分内建类型,如字典的深入讲解归入了第3章。在第4章面向对象设计部分,考虑到相应的学习阶段课时、Python的混合风格特性和简洁的面向对象语法,笔者决定精简篇幅而非长篇大论。另外,由于在Python中对象模型和面向对象调用风格的无处不在,许多相关知识已经散见于前3章,也没必要再次重复讲述。
习题
本书没有单独整理的“习题集”。首先是因为笔者水平所限,并且时间也有限,无法设计大量的原创习题。另一个原因是笔者反对采用在语言学习阶段使用“刷题”的方式提升编程能力①。读者如能将本书示例及每小节末尾的“思考和扩展练习”完成,在笔者看来就足够了。如果读者在完成这些内容后仍觉不足,不要把精力浪费在所谓的“习题集”或“面试宝典”上,而应该去学习更为深入的主题(如操作系统、网络、算法或编译原理等),或投身于解决实际问题。
参考文献
本书中提到的若干算法、观点、文档和源码的原始出处在书尾以参考文献的形式给出。阅读原始文献是非常有必要的。通过对这些文献的阅读,即便是少量阅读,学习者也能够体会到创造者当时的心境。这看似增加了额外的学习任务,但实为捷径。为了便于读者检索,笔者用[1][2]的编排格式给出了所对应的参考文献序号。
版本
笔者在Mac计算机上完成了绝大部分的写作工作。写作时的Python最新版本是3.7。当然这绝不意味着读者也需要搞一套这样的软硬件环境。使用Linux的读者不用太过担心兼容性问题。使用Windows系统的读者除去1.6.3节中管道行的相关命令外,应当可以顺利运行本书中的其他程序。笔者平时并不使用Windows系统,所以本书示例在Windows平台上可能会稍有更多的兼容性问题。但跨平台的兼容性带来的麻烦远不及Python本身升级带来的兼容性问题。所以在学习时纠结于应当使用哪种操作系统平台是意义不大的,因为Python未来的某次升级可能导致本书代码无法运行的情况更多。好在互联网的便利使得笔者对本书出版后做勘误和代码更新很容易,笔者会将本书的勘误信息和最新的升级信息反馈给出版社,读者可在出版社的官网上找到本书,获取这些资料。
格式约定
为了便于读者学习,本书坚持给出绝大多数示例的完整代码及运行效果。有的代码示例直接运行即可看到期望的结果,而有的代码则需要进行一些样例输入或者将其导入后调用。本书统一将这些运行结果或运行示例称为“程序运行结果”。
本书用以下格式表示代码实例。这些实例大部分能够直接运行,少部分需要读者自行补充完整。
本书用以下格式表示交互执行的过程和结果。
其中, $ 表示操作系统的终端(shell)提示符,>>>表示Python的交互式执行环境提示符。该格式还用来排版批量的文本,例如:
至于在说明文字中用到的解释性代码,则直接用如下格式排版:
本书的代码参考Python的推荐风格PEP-8,但有时为了行文紧凑也会缩减空格及空行。
获取配书资料
本书提供以下配书资料:
? 配套教学视频;
? 实例源代码文件;
? 教学PPT。
这些资料需要读者自行下载。请登录华章公司网站,在该网站上搜索到本书,然后单击“资料下载”按钮,即可在本书页面上找到下载链接。
读者对象
本书的最佳读者设定是有教师指导的程序设计初学者,笔者称之为“教科书”设定。这意味着本书不会教读者诸如安装执行环境和使用代码编辑器这类准备工作。这是教师的职责。如果读者是自学者,那么你应当首先确保自己正确地安装了Python运行环境,并尝试编辑一两个Python小程序运行一下。这在今天很容易,读者只需要随便找一个在线视频,看上二三十分钟即可。对于自学者来说,找到几位Python的使用者去获取一些初始建议,并且能够在学习遇到困惑时求助也是非常重要的。很多人在学习过程中因为某些不经意的障碍而放弃。比如原本代码写对了,但某个执行路径不对,在反复检查代码后依然失败而信心全无。
本书还特别适合一类读者,即学过一些程序设计语言的粗浅知识,马马虎虎地写过一些程序,处于某种目的又希望认真、正式地学习程序设计的学生。他们有可能是打算大学毕业希望找一份IT工程师工作的学生,也可能是中学阶段学过一些编程后希望在这条路上走得更远的学生。对这类读者来说,本书将带给他们更加深厚的基础能力。
致谢
本书的写作得到了好友姜寒先生的大力协助。姜先生有丰富的实践经验,又兼通各种主流程序设计语言。笔者在写作本书时多有疑难困阻,或关于Python本身,或关于其他广泛主题;又有时在详略取舍上难以决断,或是对一些观点信心不足。每每及此,与姜先生讨论后总能破除心中所障。除此之外,姜先生还审阅了本书的部分章节,从内容到行文,均提出了宝贵意见。
张頔于北京
本书从比较特别且实用的角度入手,既可以让初学者能够比较容易地进入程序设计领域,也可以让有经验的开发人员能够从中汲取一些新的营养,是一本值得每一个Python程序设计初学者及爱好者仔细研读几遍的好书。
——资深程序员 姜寒
不知不觉使用Python已有11余载。未曾想它能够成为目前*火爆的计算机语言之一。程序员应该积极地去拥抱它。学习Python编程,需要一本好的Python图书,它可以让你的学习事半功倍。虽然市面上的Python图书已经汗牛充栋,但是本书却与众不同。它是作者多年从事Python教学和开发经验的总结,内容丰富,重点突出,讲解由浅入深、循序渐进,编排风格独特,不仅适合读者自学,而且也适合各院校的教学,是一本不可多得的Python优秀图书。
——Feon与Simchain框架开发者/资深Python程序员 裴尧尧 博士
第1章 基 础
本章将介绍程序设计的入门方法,主要分为以下3个阶段进行介绍。
第1阶段:1.1~1.6节
这部分介绍最基本的知识,如历史(1.1节)、表达式(1.2节)、运行程序(1.3节)、内建类型(1.4节)、流程控制结构(1.5节)和输入输出(1.6节)。这部分内容力图精简(尤其是内建类型一节),其目的在于让学习者尽快进入编程训练中。
本阶段还会简要介绍函数的定义方法(1.5.6节),以便于在示例和练习中使用。完整论述函数这一主题则是第2章将要介绍的内容。本节介绍的内建类型(列表、字典等)则还会在第3章中深入讨论。
第2阶段:1.7~1.9节
本阶段通过一组编码实例展示前述程序设计基本方法的应用。学习者,尤其是初学者,在本阶段应进行一定数量的编码练习。本阶段的示例又分为两个层次:
? “直来直去”的程序(1.7节);这部分程序的核心是将实际问题(以绘图和数学计算为主)转换成代码进行解决。解决这类问题需要具备基本的编码知识和对问题的准确理解。本节的目的在于展示基本方法(如循环分支)的应用。
? “有技巧”的程序(1.8节);这部分程序用到了编程中最基本的模型和算法,即状态机、栈、队列和搜索等。这些技巧虽然基础但不简单。在初学阶段了解甚至掌握这些技术带来的好处是巨大的。
本阶段的最后引入了算法复杂度的初步概念及标记方法(1.9节)。
第3阶段:1.10~1.11节
本阶段主要讲述了异常处理机制(1.10节)。这是写出Python完整代码必不可少的知识。优秀的工程师能够全面地考虑各种意外情况并加以处理,从而设计出健壮的系统。在实际工作中,异常处理的重要性足以比肩本章其他全部内容,然而在初学阶段不便展开,故本部分篇幅短小,并设置为“节”归入第1章。
此外,本阶段还简要介绍了PDB调试工具(1.11节)。
1.1 历 史
在莱布尼茨①(1646~1716)生活的时代,哲学家们开始研究计算问题。人们希望能够“发现一种普遍化的数学,能用来以计算代替思考”②。1847年,英国数学家布尔建立了“布尔代数”,他创造了一套符号系统和运算法则,利用代数方法研究逻辑问题,奠定了数理逻辑的基础。19世纪70年代,人类进入电气时代。从此人类文明开始不断寻找能够完成布尔运算的单元器件,从继电器到电子管、晶体管,再到量子比特。1936年,英国数学家、逻辑学家艾伦•图灵提出了著名的图灵机模型,一台具有以下性质的机器:
? 无限线性存储器;
? 一个可移动的读写头;
? 内部具有有限个状态寄存器;
? 接受一系列有限指令进行动作。
图灵模型是计算机时代开启的标志。第二次世界大战则加速了人类在这条道路上的探索进程。战后,数学家从具体的计算问题(如密码破译、原子弹爆炸)中解放出来,思考计算机器的统一结构。1946年,美籍匈牙利数学家冯•诺依曼提出了奠定现代电子计算机基础的结构:
? 拥有算术逻辑单元和寄存器的中央处理单元;
? 指令寄存器和程序计数器;
? 存储数据和指令的内存;
? 用于存储大量数据的外部存储器;
? 输入/输出系统。
这个结构被命名为冯•诺依曼结构(或普林斯顿结构)。从此科学家和工程师们大展身手。从微观上,科学家们不断寻找更好的开关器件,以实现布尔逻辑的级联运算。从宏观上,工程师们不断地构建出更加庞大的软/硬件体系。在20世纪50~70年代,以Dijkstra为代表的计算机科学家们系统性地发展和建设了这门学科。程序设计和操作系统等领域开始出现完整的理论体系。
随着计算机技术的发展,各种各样的程序设计语言被创造出来。同其他世界性人类语言(如英语、汉语)一样,程序设计语言也将不同种族和不同生活习惯的人们联系起来。
Python诞生于1991年,设计者是Guido van Rossum。时至今日,它已经成为最流行的语言之一。相较其他主流语言(C/C 、Java、C#),其发展壮大颇具传奇色彩,而其性能并不出众的事实又为这传奇增色三分。仅从这一点看,Python就是值得学习的程序设计语言。
【思考和扩展练习】
(1)检索互联网,了解“编译型程序”和“解释型程序”,各有何优劣。
(2)Python程序属于编译型程序,还是解释型程序?根据检索结果,你对即将学到的Python语言有何预期?
1.2 表 达 式
人们天天都在做计算。在计算机科学中,“计算”一词的概念更为宽泛,用计算机做的一切事情都可以称为计算。表达式是计算的基本概念之一。当儿童学习1 2=3时,就是在学习表达式的计算。1和2是运算数(operand),加号是运算符(operator),计算结果3被称为表达式的值。得到结果的过程称为表达式的求值过程。在正式开始动手编码之前,我们先来了解一些表达式的基本概念。
1.2.1 运算数
运算数可以是1或2.5这样的数,也可以是”abc”这样的字符串。这种直接就能表示某个值的标记被称为字面值(literal)。运算数也可以是某种标记所代表的对象,比如a、s这样的标识符(identifier)。在使用标识符之前,要将其和某个对象进行关联,比如赋值操作a=5, s=”abc”。
1.2.2 运算符
狭义的运算符是指程序设计语言定义的一系列特殊符号,从四则数学运算,到各种语言常见的索引运算符[ ],以及部分语言特有的lambda运算符等。广义运算符则包含进行各种操作的函数,如求最大值的函数max(),或者切分字符串的函数split()。运算符和运算数组成表达式,如1 2。
如果和运算符配合使用的运算数是两个,就称该运算符是二元(binary)运算符,其运算是二元运算。其他运算数个数的运算符也有类似称呼,如一元和三元等。
1.2.3 表达式的风格
运算符和运算数组成表达式。运算符和运算数的出现次序会影响表达式乃至程序设计语言的风格。
1.前缀表达式
前缀,是指运算符的位置在前。前缀风格的一个例子是函数调用,如求最大值函数max(3,2,5)。函数max接收若干个运算数,计算其中最大者作为表达式的值。这种前缀函数调用形式称为面向过程的函数调用风格。
1 2也可以写为前缀形式( 1 2)。Python不使用这种形式,但著名的程序设计语言Lisp就使用这种形式。①1
2.中缀表达式
中缀,顾名思义是指运算符的位置在中间。1 2毫无疑问属于中缀表达式,但更值得注意的是面向对象风格的函数调用,如”hello Python world”.split(” “)。这个表达式里的运算是split函数。这个函数接受2个参数:第1个是字符串”hello Python world”,第2个是空格字符串” “。计算的过程则是以空格为分隔符切割字符串,得到一个包含切割结果的列表[“hello”, “Python”, “world”]。
面向过程和面向对象风格的函数调用在Python中都有广泛应用。本书从开始就普遍使用这两类风格的函数调用。本书将在第2章详细介绍函数的内容,在第4章详细介绍面向对象设计的内容。
3.后缀表达式
(1 2 )是后缀表达式。后缀表达式和人们在进行竖式演算的书写次序一致(先写数字,再写运算符,然后计算结果),如图1.1所示。
图1.1 竖式运算
某些高级计算器支持以后缀次序输入算式,如HP48G。在程序设计语言的语法规则中,后缀序比较少见。本书在1.8.5节的示例中使用了后缀表达式。
1.2.4 表达式的嵌套
复杂的表达式可以由简单表达式和运算符组合而成。12是表达式,它可以进一步和乘法运算符组合成12*3,或者和加法运算符组合成12 3。乘法运算的优先级较高。括号用来改变运算符的运算次序1*(2 3)。表达式自左向右计算(这对减法和除法是至关重要的),这称为运算符的结合性。
这是小学生就明白的事情,但计算机科学家们感兴趣的是如何严谨地描述上述说明。在计算机科学中,往往使用如下范式来准确定义表达式(为了方便理解,这里只讨论由数字、加号、乘号和括号组成的四则运算表达式)。
初级表达式 是 数字 或 (四则表达式)①
乘除表达式 是 初级表达式 或
乘除表达式 * 初级表达式
乘除表达式 / 初级表达式
四则表达式 是 乘除表达式 或
乘除表达式 四则表达式
乘除表达式 – 四则表达式
如果读者之前从未接触过这种形式的定义,那着实要动一番脑筋才能理解。请读者仔细体会上述描述,该描述明确、完整地包含了关于四则运算表达式各个方面的说明。
上述描述表达式的一般形式被称为巴克斯范式(Backus Normal Form),这是计算机科学中用来描述语法的基本模型。精确地将问题描述成某种模型,对解决问题意义重大。比如描述成巴克斯范式的语法解析问题,可以很容易地使用bison这类语法解析器来处理。②
在上述定义中充满了用事物自身定义自身的方法,这种方法称为递归。递归是一种非常重要的程序设计方法。本书将在2.4节对其进行详细介绍。
1.2.5 数据类型
运算符的行为取决于运算数的类型。例如,字符串类型也可以做加法和乘法:
“123” “456” 的值是 “123456”
“123” * 2 的值是 “123123”
这两种字符串运算分别是拼接和重复。同样的运算符有不同的行为,这称为运算符重载(overloading)。在编程实践中,程序员经常受益于这种便利。本书将在2.5.5节讲述如何针对自定义类型重载运算符。
1.2.6 副作用
在表达式的求值过程中,对状态的改变称为表达式的副作用。Python中内建的各种运算符(此处是狭义的含义,如加、减、乘、除、比较等运算符,并不包含用户自定义的运算符或函数)是没有副作用的,但各种函数调用时常带有副作用(比如各种输入、输出函数)。在使用带有副作用的表达式构建复杂表达式时要格外留意,因为这可能带来程序员容易忽视的行为。例如:
if expA and expB :
…
这条语句用来测试表达式A和B都为真的条件。expA and expB的计算具有短路性质,即如果A为假,则整个表达式已然能够判断为假,表达式B不会被求值。如果表达式B包含函数调用,则意味着该函数不一定被调用。
不过总体说来,Python中副作用带来的麻烦并不多。程序员只要不在复杂表达式中嵌套带有副作用的函数即可避免这些容易混淆的情形。这种编码风格也能很容易遵守。①
1.2.7 小结
为什么要在一开始讨论1 1=2这些简单的内容,而非动手写一些立刻就能运行的代码呢?原因在于,清晰的核心概念是持续学习的保证。看似纷繁的知识其实都有着清晰的图景,司空见惯的简单背后隐藏着本质的原理。在程序设计语言中,称得上核心的概念极为有限。学习的过程就是对这些核心概念的认识不断提高的过程。
表达式种类繁多,工程师要花费相当多的精力在处理和设计各种表达式上。清晰地理解表达式的本质特性,能让学习者迅速抓住语言特点,进而顺利地掌握用这门语言进行程序设计的方法。
评论
还没有评论。