目录¶
第一章: 培养 Pythonic 思维¶
Abstract
Pythonic 风格的代码,是开发者在使用 Python 语言编程并相互协作的过程中逐渐形成的。本章讲解如何采用这样的风格编写常见的Python代码。
- 1 查询自己使用的 Python 版本
- 2 遵循 PEP 8 风格指南
- 3 了解
bytes
与str
的区别 - 4 用支持插值的
f-string
取代 C 风格的格式字符串与str.format
方法 - 5 用辅助函数取代复杂的表达式
- 6 把数据结构直接拆分到多个变量里,不要专门通过下标访问
- 7 尽量用
enumerate
取代range
- 8 用
zip
函数同时遍历两个迭代器 - 9 不要在
for
与while
循环后面写else
- 10 用赋值表达式减少重复代码
第二章: 列表与字典¶
Abstract
在Python语言中处理信息时,最常用的方法是把一系列数值保存到列表(list)中。另外一种跟它互补的结构,是字典(dict),字典可以把它存储的查找键(key)映射到对应的值(value)上。本章讲解如何采用这些数据结构来编写程序。
- 11 学会对序列做切片
- 12 不要在切片里同时指定起止下标与步进
- 13 通过带星号的
unpacking
操作来捕获多个元素,不要用切片 - 14 用
sort
方法的key
参数来表示复杂的排序逻辑 - 15 不要过分依赖给字典添加目时所用的顺序
- 16 用
get
处理键不在字典中的情况,不要使用in
与KeyError
- 17 用
defaultdict
处理内部状态中缺失的元素,而不要用setdefault
- 18 学会利用
__missing__
构造依赖键的默认值
第三章: 函数¶
Abstract
Python中的函数具备多种特性,这有助于简化编程工作。Python函数的某些性质与其他编程语言中函数的类似,但也有很多是Python独有的。本章介绍如何使用函数来表达开发者的意图,如何让代码更容易复用,以及如何减少bug。
- 19 不要把函数返回的多个数值拆分到三个以上的变量中
- 20 遇到意外状况时应该抛出异常,不要返回
None
- 21 了解如何在闭包里面使用外围作用域中的变量
- 22 用数量可变的位置参数给函数设计清晰的参数列表
- 23 用关键字参数来表示可选的行为
- 24 用
None
和docstring
来描述默认值会变的参数 - 25 用只能以关键字指定和只能按位置传入的参数来设计清晰的参数列表
- 26 用
functools.wraps
定义函数修饰器
第四章 推导与生成¶
Abstract
Python有一种特殊的语法,可以迅速迭代列表(list)、字典(dict)与集合(set),并据此生成相应的数据结构,这也使得在函数返回的这种结构上,逐个访问根据原结构所派生出来的一系列值。本章讲解怎样利用这种机制来提升程序效率并降低内存用量,同时提高代码可读性。
- 27 用列表推导取代
map
与filter
- 28 控制推导逻辑的子表达式不要超过两个
- 29 用赋值表达式消除推导中的重复代码
- 30 不要让函数直接返回列表,应该让它逐个生成列表里的值
- 31 谨慎地迭代函数所收到的参数
- 32 考虑用生成器表达式改写数据量较大的列表推导
- 33 通过
yield from
把多个生成器连起来用 - 34 不要用
send
给生成器注入数据 - 35 不要通过
throw
变换生成器的状态 - 36 考虑用
itertools
拼装迭代器与生成器
第五章 类与接口¶
Abstract
Python是面向对象的语言。用Python编程时,经常要编写新的类,而且还要定义这些类应该如何通过其接口以及继承体系与其他代码相交互。本章讲解怎样使用类来表达对象所应具备的行为。
- 37 用组合起来的类来实现多层结构,不要用嵌套的内置类型
- 38 让简单的接口接受函数,而不是类的实例
- 39 通过
@classmethod
多态来构造同一体系中的各类对象 - 40 通过
super
初始化超类 - 41 考虑用
mix-in
类来表示可组合的功能 - 42 优先考虑用
public
属性表示应受保护的数据,不要用private
属性表示 - 43 自定义的容器类型应该从
collections.abc
继承
第六章 元类与属性¶
Abstract
元类(metaclass)与动态属性(dynamic attribute)都是很强大的Python特性,但它们也有可能会让程序出现古怪的行为与意外的效果。本章讲解这些机制的习惯用法,确保最终写出来的代码遵循最小惊讶原则(rule of least surprise)。
- 44 用纯属性与修饰器取代旧式的
setter
与getter
方法 - 45 考虑用
@property
实现新的属性访问逻辑,不要急着重构原有的代码 - 46 用描述符来改写需要复用的
@property
方法 - 47 针对惰性属性使用
__getattr__
、__getattribute__
及__setattr__
- 48 用
__init_subclass__
验证子类写得是否正确 - 49 用
__init_subclass__
记录现有的子类 - 50 用
__set_name__
给类属性加注解 - 51 优先考虑通过类修饰器来提供可组合的扩充功能,不要使用元类
第七章 并发与并行¶
Abstract
用Python很容易写并发程序,这种程序可以在同一时刻做许多件不同的事情。Python也可以通过系统调用、子进程以及C语言扩展来实现并行处理。本章介绍在这些不同情况下,如何充分利用 Python 的相关特性。
- 52 用
subprocess
管理子进程 - 53 可以用线程执行阻塞式 I/O,但不要用它做并行计算
- 54 利用
Lock
防止多个线程争用同一份数据 - 55 用
Queue
来协调各线程之间的工作进度 - 56 学会判断什么场合必须做并发
- 57 不要在每次 fan-out 时都新建一批
Thread
实例 - 58 学会正确地重构代码,以便用
Queue
做并发 - 59 如果必须用线程做并发,那就考虑通过
ThreadPoolExecutor
实现 - 60 用协程实现高并发的 I/O
- 61 学会用
asyncio
改写那些通过线程实现的 I/O - 62 结合线程与协程,将代码顺利迁移到
asyncio
- 63 让
asyncio
的事件循环保持畅通,以便进一步提升程序的响应能力 - 64 考虑用
concurrent.futures
实现真正的并行计算
第八章 稳定与性能¶
Abstract
Python内置了一些功能与模块,可以让程序变得更加可靠。另外,Python还提供了一些工具,可以让我们轻松地提升程序的性能。本章讲解怎样用Python优化程序,让这些程序在正式的运行环境中表现得更加稳定、更加高效。
- 65 合理利用
try
/except
/else
/finally
结构中的每个代码块 - 66 考虑用
contextlib
和with
语句来改写可复用的try
/finally
代码 - 67 用
datetime
模块处理本地时间,不要用time
模块 - 68 用
copyreg
实现可靠的pickle
操作 - 69 在需要准确计算的场合,用
decimal
表示相应的数值 - 70 先分析性能,然后再优化
- 71 优先考虑用
deque
实现生产者-消费者队列 - 72 考虑用
bisect
搜索已排序的序列 - 73 学会使用
heapq
制作优先级队列 - 74 考虑用
memoryview
与bytearray
来实现无须拷贝的bytes
操作
第九章 测试与调试¶
Abstract
不管使用哪种语言编程,都应该把写出来的代码测试一下。但对于Python来说,还有个特殊的问题,那就是它所提供的动态机制可能会增加程序在运行时出现错误的风险。好在Python也让我们可以比较容易地编写测试代码和故障诊断程序。本章讲解怎样用Python内置的工具来测试并调试程序。
- 75 通过
repr
字符串输出调试信息 - 76 在
TestCase
子类里验证相关的行为 - 77 把测试前、后的准备与清理逻辑写在
setUp
、tearDown
、setUpModule
与tearDownModule
中,以防用例之间互相干扰 - 78 用
Mock
来模拟受测代码所依赖的复杂函数 - 79 把受测代码所依赖的系统封装起来,以便于模拟和测试
- 80 考虑用
pdb
做交互调试 - 81 用
tracemalloc
来掌握内存的使用与泄漏情况
第十章 协作开发¶
Abstract
如果许多人要协作开发Python程序,那就得仔细商量代码的写法了。即便只是个人独立开发,也需要了解如何使用其他人所写的模块。本章介绍标准的工具以及业界总结出来的最佳方法,来说明怎样协作开发Python程序。
- 82 学会寻找由其他 Python 开发者所构建的模块
- 83 用虚拟环境隔离项目,并重建依赖关系
- 84 每一个函数、类与模块都要写
docstring
- 85 用包来安排模块,以提供稳固的 API
- 86 考虑用模块级别的代码配置不同的部署环境
- 87 为自编的模块定义根异常,让调用者能够专门处理与此 API 有关的异常
- 88 用适当的方式打破循环依赖关系
- 89 重构时考虑通过
warnings
提醒开发者 API 已经发生变化 - 90 考虑通过
typing
做静态分析,以消除 bug