用functools.wraps定义函数修饰器
python为修饰器提供了专门的语法,它使得程序在运行的时候,能够用一个函数来修饰另一个函数
对于调试器这种依靠内省机制的工具,直接编写修饰器会引发奇怪的行为
内置的functools模块提供了名为wraps的修饰器,开发者在定义自己的修饰器时,应该用wraps对其做一些处理以避免问题
以contextlib和with语句改写可复用的try finally代码
可用with语句改写try/finally块中的逻辑,以便提升复用程度,并使代码更为整洁
内置的contextlib模块提供了名叫contextmanager的修饰器,开发者只需要用它来修饰自己的函数,即可令该函数支持with语句
情景管理器可以通过yield语句向with语句返回一个值,此值会赋给由as关键字所指定的变量。
该机制阐明了这个特殊情景的编写动机,并令with块中的语句能够直接访问这个目标变量
用copyreg实现可靠的pickle操作
内置的pickle模块能把python对象序列化为字节流,也能把字节反序列化为python对象
但其序列化数据采用的是不安全的格式,所以只适合在彼此信任的程序之间
(json模块产生的数据是安全的模式)
如果用法比较复杂,pickle模块的功能也许会出现问题
我们可以把内置的copyreg模块同pickle结合起来使用,
以便为旧数据添加缺失的属性值、进行类的版本管理,并给序列化之后的数据提供固定的引入路径。
用datetime处理本地时间
#协调世界时(coordinated universal time,UTC)是一种标准的实践表达方式,与时区无关
from datetime import datetime,timezone
'''
不要用time模块在不同时区间转换
若要在不同时区之间可靠的执行转换操作,应把内置的datetime模块与开发者社区提供的pytz模块搭配起来使用
开发者总是应该先把时间表示出UTC格式,然后对其执行各种转换操作,最后再转回本地时间
'''
使用内置算法与数据结构
#1.双向队列
#FIFO 时间O(1)
from collections import deque
fifo = deque()
fifo.append(1)
x = fifo.popleft()
#2.有序字典
#标准字典无序因为实现方式是快速哈希表
from collections import OrderedDict
a = OrderedDict()
a['foo'] = 1
a['bar'] = 2
b = OrderedDict()
b['foo'] = 'red'
b['bar'] = 'blue'
for value1,value2 in zip(a.values(),b.values()):
print(value1,value2)
#>>(1, 'red')
# (2, 'blue')
#3.带有默认值的字典
from collections import defaultdict
stats = defaultdict(int)
stats['my_counter'] += 1
#4.堆队列(优先级队列)
#堆heap是一种数据结构
#heapq模块提供了heappush、heappop、nsmallest等函数,复杂度与列表长度对数成正比
#5.二分查找
#bisect模块中的bisect_left等函数,提供了高效的二分折半搜索算法
#其返回的索引代表待搜寻的值在序列中的插入点
from bisect import *
#i = bisect_left(x,991234)
#复杂度为对数级别
#6.与迭代器有关的工具
#内置的itertools模块中包含大量函数,可以组合并操控迭代器
import itertools
help(itertools)
'''
我们应该用python内置的模块来描述各种算法和数据结构
开发者不应该自己去重新实现那些功能,因为我们很难把它写好
'''
使用decimal获得更高精确度
from decimal import Decimal
rate = Decimal('1.45')
print(rate)
'''
对于编程中可能用到的每一种数值,我们都可以拿对应的python内置类型或内置模块中的类表示
Decimal类非常适合对精度要求很高,且对舍入行为要求很严的场合,例如货币计算等。
'''
为每个函数、类、模块编写文档字符串
def palindrome(i):
"""return true if the given i is a palindrome"""
return i == i[::-1]
print(repr(palindrome.__doc__))
#通过__doc__属性返回文档
#每个模块、类、函数都应有顶级的docstring
#放在最前面,第一行一般描述功能,且用双引号
'''
通过docstring编写文档,修改代码时也要更新文档
文档应介绍本模块的内容,且列出重要函数
为类编写文档时应在class语句下的docstring介绍本类的行为、属性,及子类该实现的方法
为函数的docstring应介绍每个参数,函数的返回值,函数在执行中可能抛出的异常等。
'''
用包安排模块,并提供稳固API
#当模块太多,需要在程序中引入抽象层,使代码更易于理解。
#python的包就可以充当抽象层。包是一种含有其他模块的模块。
'''
包的两大主要用途:
1.命名空间:
可用包把代码划分成各自独立且互不冲突的名称空间,使每块代码都能具备独有的绝对模块名称
2.稳固的API
把外界可见的名称,列在名为__all__的特殊属性中,即可为包提供一套明确的API
'''
'''
只要把__init__.py文件放入含有其他源文件的目录里,就可以将该目录定义为包。
目录中的文件,都将成为包的子模块。该包目录下也可能有其他包。
若想隐藏某个包的内部实现,可在包的__init__.py文件中,只把外界可见的那些名称引入进来,或是给仅限内部使用的那些名称添加下划线前缀
若软件包只在开发团队或代码库内部使用,那可能没有必要通过__all__来明确导出API
'''
为自编模块定义根异常
为模块定义根异常,可以把API调用者与模块的API相隔离
调用者在使用API时可以通过捕获根异常,来发现调用代码中隐藏的bug
调用者可以通过捕获python的Exception基类,来帮助模块的研发者找寻API实现代码中的bug
可以从模块的根异常里继承一些中间异常,并令API的调用者捕获这些中间异常。
这样模块开发者将来就能在不破坏原有调用代码的前提下,为这些中间异常分别编写具体的异常子类。
打破循环依赖关系
#避免循环依赖三种方法:
#1.调整引入顺序
#2.先引入、再配置、最后运行
#3.动态引入
'''
最佳方法是把导致两个模块相互依赖的那部分代码,重构为单独的模块,并把它放在依赖树的底部。
最简方法是执行动态的模块引入操作,这样既可缩减重构的精力,也能降低代码复杂度。
'''
通过repr字符串输出调试信息
#print隐藏了类型信息
#repr字符串传给内置的eval函数可以还原为初始的那个值
#在格式化字符串里使用%r,能够产生与repr函数的返回值相仿的可打印字符串
#可在任意对象上查询__dict__属性,观察其内部信息
用unittest测试全部代码
#内置的unittest模块提供了测试者所需的很多功能
#可在TestCase子类中为每一个要测试的行为定义对应的测试方法
#TestCase子类里的测试方法,其名称必须以test开头
考虑用pdb实现交互调试
'''
我们可以修改程序,在想要调试的代码上方直接加入import pdb;pdb.set_trace()语句,以触发调试器。
python调试器也是一个完整的python提示符界面,我们可以检视修改受测试的程序状态。
可在pdb提示符中输入命令,以便精确控制程序的执行流程,这些命令使得我们能够交替地查看程序状态并继续向下运行程序。
'''
先分析性能再优化
#因为python程序的性能瓶颈很难直接观察出来
#性能分析使用cProfile模块而不用profile,因为前者能给出更精确的性能分析数据