前言
结缘Python
我初次接触Python是在2008年末。那时临近大学毕业,我凭着在学校里学到的一丁点儿Java知识四处求职。我从大学所在的城市南昌出发去了北京,借宿在一位朋友的出租屋里,他当时在巨鲸音乐网上班,用的主要编程语言正是Python。
得知我正在寻找一份Java相关的工作,那位朋友跟我说:“写Java代码有啥意思啊?Python比Java好玩多了,而且功能还特别强大,连Google都在用!”
在他的热情“传道”下,我对Python语言产生了好奇心,于是找了一份当时最流行的开源教程Dive into Python,开始学起Python来。
实话实说,之前在学校用Java和C语言编程时,我很少体会到编程的快乐,也从未期待过自己将来要以写代码为生。但神奇的是,在学了一些Python的基础知识,并用它写了几个小玩意儿以后,我突然意识到原来自己很喜欢编程,并开始期待找到一份以Python为主要编程语言的开发工作——也许这就是我和Python之间的缘分吧!
幸运的是,在当时的CPyUG(中国Python用户组)邮件组里,正好有一家南昌的公司在招聘全职Python程序员。看到这个消息后,我立马做出了决定:结束短暂的“北漂”生活,回到学校准备该职位的面试。后来,我成功通过了面试,最终在那家公司谋得了一份Python开发的实习工作,并从此开启了后来十余年的Python编程生涯。
为什么写这本书
回顾自己的从业经历,我从中发现一件有意思的事:编程作为一项技能,或者说一门手艺,给新手带来的“蜜月期”非常短暂。
一开始,我们对一门编程语言只是略懂些皮毛,只要能用它实现想要的功能,就会非常开心。假如再学会语言的一些高级用法,比如Python里的装饰器,把它应用在了项目代码里,我们便整天乐得合不拢嘴。
但欢乐的时光总是特别短暂,一些类似的遭遇似乎总会不可避免地降临到每个人头上。
在接手了几个被众人称为“坑”的老项目,或是亲手写了一些无人敢接手的代码后;在整日忙着修bug,每写一个新功能就引入三个新bug后……夜深人静之时,坐在电脑前埋头苦干的我们总有那么一些瞬间会突然意识到:编程最初带给我们的快乐已悄然远去,写代码这件事现在变得有些痛苦。更有甚者,一想到项目里的烂代码,每天起床后最想干的一件事就是辞职。
造成上面这种困境的原因是多方面的,而其中最主要、最容易被我们直观感受到的问题就是:烂代码实在是太多了。后来,在亲历了许多个令人不悦的项目之后,我才慢慢看清楚:即便两个人实现同一个功能,最终效果看上去也一模一样,但代码质量却可能有着云泥之别。
好代码就像好文章,语言精练、层次分明,让人读了还想读;而烂代码则像糊成一团的意大利面条,处处充斥着相似的逻辑,模块间的关系错综复杂,多看一眼都令人觉得眼睛会受伤。
在知道了“代码也分好坏”以后,我开始整日琢磨怎么才能把代码写得更好。我前前后后读过一些书——《代码大全》《重构》《设计模式》《代码整洁之道》——毫无疑问,它们都是领域内首屈一指的经典好书,我从中学到了许多知识,至今受益匪浅。
这些领域内的经典图书虽好,却有个问题:它们大多是针对Java这类静态类型语言所写的,而Python这门动态类型的脚本语言又和Java大不一样。这些书里的许多理念和例子,假如直接套用在Python里,效果不尽如人意。
于是,话又说回来,要写出好的Python代码,究竟得掌握哪些知识呢?在我看来,问题的答案可分为两大部分。
·第一部分:语言无关的通用知识,比如变量的命名原则、写注释时的注意事项、写条件分支语句的技巧,等等。这部分知识放之四海而皆准,可以运用在各种编程语言上,不光是Python。
·第二部分:与Python语言强相关的知识,比如自定义容器类型来改善代码、在恰当的时机抛出异常、活用生成器改善循环、用装饰器设计地道的API,等等。
当然,上面这种回答显然过于简陋,略去了太多细节。
为了更好地回答“如何写出好的Python代码”这个问题,从2016年开始,我用业余时间写作了一系列相关的技术文章,起名为“Python工匠”——正是这十几篇文章构成了本书的骨架。此外,本书注重故事、注重案例的写作风格也与“Python工匠”系列一脉相承。
如果你也像我一样,曾被烂代码所困,终日寻求写好Python程序的方法,那么我郑重地将本书推荐给你。这是我多年的经验汇集,相信会给你一些启发。
目标读者
本书适合以下人群阅读:
·以Python为主要开发语言的工程师
·工作中需要写一些Python代码的工程师
·有其他语言编程经验、想学习如何写出高效Python代码的工程师
·任何爱好编程、喜欢Python语言的读者
本书内容以进阶知识为主。书里虽有少量基础知识讲解,但并不全面,描述得也并不详尽。正因如此,假如你从未有过任何编程经验,我并不建议你通过本书来入门Python。
在Python入门学习方面,我推荐由人民邮电出版社图灵公司出版的《Python编程:从入门到实践》。当你对Python有了一些了解、打好基础后,再回过头来阅读本书,相信彼时你可以获得更好的阅读体验。
代码运行环境
如果没有特殊说明,书中所有的Python代码都是采用Python 3.8版本编写的。
结构与特色
五大部分
全书共计13章,按内容特色可归入五大部分。
第一部分 变量与基础类型由第1章、第2章和第3章组成。在学习一门编程语言的过程中,“如何操作变量”和“如何使用基础类型”是两个非常重要的知识点。通过学习这部分内容,你会习得如何善用变量来改善代码质量,掌握数值、字符串及内置容器类型的使用技巧,避开常见误区。
第二部分 语法结构由第4章、第5章和第6章组成。条件分支、异常处理和循环语句是Python最常见的三种语法结构。它们虽然基础,但很容易被误用,从而变成烂代码的帮凶。本部分内容会带你深入这三种语法结构,教你掌握如何用它们简洁而精准地表达逻辑,写出高质量的代码。
第三部分 函数与装饰器由第7章和第8章组成。函数是Python语言最重要的组成要素之一。正是因为有了函数,我们才获得了高效复用代码的能力。而装饰器则可简单视为基于函数的一种特殊对象——它始于函数,但又不止于函数。这两章介绍了许多与函数和装饰器有关的“干货”,掌握它们,可以让你在写代码时事半功倍。
第四部分 面向对象编程由第9章、第10章和第11章组成。众所周知,Python是一门面向对象编程语言,因此“面向对象技术”自然是Python学习路上的重中之重。第9章围绕Python语言的面向对象基础概念和高级技巧展开。第10章和第11章则是为大家量身定制的面向对象设计进阶知识。
第五部分 总结与延伸由第12章和第13章组成。这部分内容可以看作对全书内容的总结和延伸。第12章汇总本书出现过的所有与“Python对象模型”相关的知识点,并阐述它们与编写优雅代码之间的重要关系。而最后的第13章则是一些与大型项目开发相关的经验之谈。
三大板块
除了第11章和第13章等少数几个纯案例章以外,其他章都包含基础知识、案例故事、编程建议三个常驻板块。
其中,基础知识板块涵盖和该章主题有关的基础知识点。举例来说,在第6章的基础知识板块,你会学习有关迭代器与可迭代类型的基础知识。不过,需要提醒各位的是,本书中的基础知识讲解并不追求全面,仅包含笔者基于个人经验挑选并认为比较关键的知识点。
假如说本书的基础知识板块与其他同类书的内容大同小异,那么案例故事与编程建议则是将本书与其他Python编程类图书区分开来的关键。
在每一个案例故事板块,你会读到一个或多个与该章主题相关的故事。比如,第1章讲述了一位Python程序员去某公司参加面试的故事,读完它,你会领会到“变量与注释”究竟是如何影响了故事主人公的面试结果,最终深刻地理解两者是如何塑造我们对代码的第一印象的。
编程建议板块主要包含一些与该章主题相关的建议。比如在第4章中,我一共介绍了7条与条件分支有关的建议。虽然内容包罗万象,但书中的所有编程建议都是围绕“如何写好代码”这件事展开的。比如,我会建议你尽量消除分支里的重复代码、避开or运算符的陷阱,等等。
除了第10章与第11章同属一个主题,有先后顺序以外,本书的每一章都是独立的。你可以随意挑选自己感兴趣的章节开始阅读。
13章内容
以下为各章内容要点。
第1章 变量与注释要写出一份质量上乘的代码,运用好变量与注释不是加分项,而是必选项。在本书的开篇章,你将学习包括动态解包在内的一些Python变量的常见用法,了解编写代码注释的几项基本原则。而本章的案例故事“奇怪的冒泡排序算法”,是全书趣味性最强的几个故事之一,请一定不要错过。
第2章 数值与字符串本章内容围绕Python中最基础的两个数据类型展开。在基础知识板块,我们会学习一些与数值和字符串有关的基本操作。在案例故事板块,你会见到一个与代码可读性有关的案例。在编程建议板块,你会学到一些与Python字节码相关的语言底层知识。
第3章 容器类型由于Python语言的容器类型丰富,因此本章是全书篇幅最长的章节之一。在基础知识板块,除了介绍每种容器的基本操作,我还会讲解包括可变性、可哈希性、深拷贝与浅拷贝在内的Python语言里的许多重要概念。在案例故事板块,你会读到一个与自定义容器类型相关的重构案例。
第4章 条件分支控制流条件分支是个让人又爱又恨的东西:少了它,许多逻辑根本没法表达;而一旦被滥用,代码就会变得不堪入目。通过本章,你会学到在Python中编写条件分支语句的一些常用技巧。在案例故事板块,我会说明有些条件分支语句其实没必要存在,借助一些工具,我们甚至能让它们完全消失。
第5章 异常与错误处理异常就像数值和字符串一样,是组成Python语言的重要对象之一。在本章中,你首先需要彻底搞清楚为什么要在Python代码里多使用异常。随后,你会邂逅两个与异常有关的案例故事,其中一个是我的亲身工作经历。
第6章 循环与可迭代对象循环也许是所有编程语言里最为重要的控制结构。要写好Python里的循环,不光要掌握循环语法本身,还得对循环的最佳拍档——可迭代对象了然于胸。在本章的基础知识板块,我会详细介绍可迭代对象的相关知识。
第7章 函数Python中的函数与其他编程语言里的函数很相似,但又有着些许独特之处。在本章中,你会学习与函数有关的一些常见技巧,比如:为何不应该用可变类型作为参数默认值、何时该用None作为返回值,等等。案例故事板块会展示一个有趣的编程挑战题,通过故事主人公的解题经历,你会掌握给函数增加状态的三种方式。在编程建议板块,你会读到一份脚本案例代码,它完整诠释了抽象级别对于函数的重要性。
第8章 装饰器装饰器是一个独特的Python语言特性。利用装饰器,你可以实现许多既优雅又实用的工具。本章的基础知识板块非常详细,教你掌握如何创建几类常用的装饰器,比如用类实现的装饰器、使用可选参数的装饰器等。在编程建议板块,我会展示装饰器的一些常见使用场景,分析装饰器的独特性所在。相信学完本章内容之后,你一定可以变身为装饰器方面的高手。
第9章 面向对象编程Python是一门面向对象编程语言,因此,好的Python代码离不开设计优良的类和对象。在这一章中,你会读到一些与Python类有关的常用知识,比如什么是类方法、什么是静态方法,以及何时该使用它们等。此外,在本章的基础知识板块,你还会详细了解鸭子类型的由来,以及抽象类如何影响了Python的类型系统。本章的案例故事是一个与类继承有关的长故事。它会告诉你为什么继承是一把双刃剑,以及如何才能避开由继承带来的问题。
第10章和第11章 面向对象设计原则要写出好的面向对象代码,经典的SOLID设计原则是我们学习路上的必经之地。在这两章里,我会通过一个大的编程实战项目诠释SOLID原则的含义。通过学习这部分内容,你会掌握如何将SOLID原则运用到Python代码中。
第12章 数据模型与描述符数据模型是最重要的Python进阶知识,或许没有之一。恰当地运用数据模型是写出高效Python代码的关键所在。本章一开始会简单回顾书中出现过的所有数据模型知识。在基础知识板块,我会对运算符重载做一些简单介绍。在案例故事板块,你会读到一个与数据模型和集合类型有关的有趣故事。
第13章 开发大型项目如何开发好一个大型项目,是个非常庞大的话题。在本章中,我精选了一些与之相关的重要主题,比如,在大型项目中使用哪些工具,能让项目成员间的协作事半功倍,提升每个人的开发效率。在此之后,我会介绍两个常用的Python单元测试工具。本章最后介绍了为大型项目编写单元测试的5条建议。希望这些内容对你有所启发。
排版约定
本书使用以下排版约定。
这个图标表示提示或建议。
这个图标表示额外的参考信息。
这个图标表示警告或提醒。
获取本书示例代码
作为一本编程图书,本书包含许多代码示例。如果你想在自己的电脑上运行这些代码,做一些简单的修改和测试,可以访问本书的图灵社区主页1下载书中所有代码示例源文件。
1也可在本书图灵社区主页查看或提交勘误。
致谢
感谢Guido van Rossum先生发明了Python语言,正是Python语言的优雅、简洁以及卓越的编码体验,点燃了我对编程的热情。
感谢我的好朋友侯成,如果没有他,我对计算机的兴趣可能会止步于电脑游戏。他带着我学习了Q-Basic和Dreamweaver等“史前”工具,让我第一次领略到了用计算机创造事物的美妙之处。
感谢我的朋友谢易,是他把Python编程语言介绍给了我。
感谢我大学毕业后的第一位领导——国内第一代Python程序员echo,他教给我许多Python编程技巧。也是从他身上,我慢慢习得了分辨好代码与坏代码的能力。
在我写作“Python工匠”系列的过程中,许多媒体转发了我的文章,帮助提高了整个系列的影响力。它们是“腾讯技术工程”知乎专栏、董伟明(@dongweiming)的Python年度榜单,以及以下微信公众号:“蓝鲸”“Python猫”“Python编程时光”“Python开发者”“腾讯NEXT学院”等。由于名单过长,如果你的媒体也曾转发过“Python工匠”系列,但没有出现在上面的列表中,还请见谅。
感谢我的前同事与朋友们。当我在朋友圈转发“Python工匠”系列文章时,他们总是毫不吝惜地给予我赞美与鼓励。虽然受之有愧,但我的确深受鼓舞。
特别感谢我在腾讯蓝鲸团队的所有同事与领导,他们在我写作“Python工匠”系列的过程中,提供了许多积极反馈,并且不遗余力地转发文章。这些善意的举动,为本书漫长而充满磨炼的写作过程,注入了强大的驱动力。
感谢我多年的朋友郝莹女士。正是时任图灵编辑的她,跟我分享了图书出版行业的许多知识,并鼓励我向出版社提交选题报告。是她的一片好意,直接促成了本书的诞生。
感谢我的编辑英子女士,她在本书的写作过程中,提供了许多专业建议。本书能有现在的结构和流畅性,离不开她的细心审阅与编辑工作。
感谢参与审阅本书初稿的所有人。他们中有些是我相识多年的同事与朋友,更多则是我从未谋面的“网友”。因慕名各位在开源世界的贡献,我邀请他们审阅本书内容,无一例外,所有人都爽快地答应了我的请求,并围绕本书的内容和结构提出了许多精准的修改意见和建议。他们是赖信涛(@laixintao)、李者璈(@Zheaoli)、林志衡(@onlyice)、王川(@fantix)、laike9m、冯世鹏(@fengsp)、伊洪(@yihong0618)、明希(@frostming)和李卫辉(@liwh)。
感谢我的兄长朱斌(dribbble ID:MVBen),作为一名专业的体验设计师,他为本书所有插图提供了好看的Sketch模板,对大部分插图做了润色,并亲手绘制了书里的所有图标。
感谢我的父母、岳父母,还有我马上读小学的女儿,他们是我写作本书的坚强后盾。
最后,特别感谢我的妻子章璐女士。她十几年如一日,坚定地支持我从事编程工作,并为此做出了许多牺牲。此外,作为本书的第一位读者,她在我写作期间提出了大量优秀建议。如果少了她的理解与支持,这本书根本不可能完成。
朱雷(@piglei),深圳
2021年7月