65. 合理利用try/except/else/finally结构中的每个代码块

将需要捕获异常的代码卸载try里面,不要写多于的代码,因为其余的代码可能也会报错,到时候就不知道是哪个代码出错了
try/except/else,这种形式是try中代码不报错的情况下才会执行else
完整的try/except/else/finally形式,finally是无论之前进入了else还是except,最终都会执行finally块中的代码

66. 考虑用contextlib和with语句来改写可复用的try/finally代码

try-finally结构的代码其实和with有点像,比如下面,with块结束后会自动关闭。

  1. try:
  2. file = open()
  3. finally:
  4. file.close()
  5. with open() as file:
  6. ...

当然,正常来说,如果要实现with这种写法,需要在对应的类中实现enterexit方法,比如open()返回的变量对应的类中需要是实现上面两个方法,而python中可使用contextlib这个库方便的让我们自己可以实现普通函数也能用with,用contextmanager装饰器。

  1. from contextlib import contextmanager
  2. import logging
  3. @contextmanager
  4. def log_func(log_level):
  5. logger = logging.getLogger()
  6. old_level = logger.getEffectiveLevel()
  7. logger.setLevel(log_level)
  8. try:
  9. # 如果需要写成with as的形式
  10. # yield 变量名 如yield logger
  11. yield
  12. finally:
  13. logger.setLevel(old_level)
  14. with log_func(logging.DEBUG):
  15. logging.debug('hello')
  16. logging.warning('nihao')
  17. logging.debug('hello')
  18. logging.warning('nihao')

67. 用datetime模块处理本地时间,不要用time模块

time模块在处理时区不稳定,依赖本地平台,因此处理时区转换等情况的时候不要用time
将datetime和pytz结合可以进行可靠的转换。

68. 使用copyreg实现可靠的pickle操作

pickle本身没有考虑安全问题,因此pickle序列化和反序列化的数据最好在可信任的双方之间使用,而序列化后的json数据是安全的。pickle存储dump和导入load前后,对应的类可能发生变化
使用copyreg可以注册pickle,使类和属性绑定

69. 在需要准确计算的场合,用decimal表示相应的数值

它的整数类型实际上可以表示任意尺寸的整型数据,它的双精度浮点数类型遵循IEEE 754规范。
根据上述规范计算的浮点数可能存在偏差,即有时候我们会得到0.99999的情况其实应该是1,比如

  1. a = 3 * 60 + 42
  2. a = a * 1.45 / 60
  3. print(a)
  4. # 准确答案应该是5.365,程序计算出来为5.36499999

如果在这种必须要准确的值的情况下,可以使用decimal,不过对于无限循环小数他也还是存在误差的,对于1/3这种情况可以使用fractions库

  1. from decimal import Decimal
  2. a = 3 * 60 + 42
  3. a = a * Decimal('1.45') / 60 # 如果传入float实例,仍然得不到准确结果Decimal(1.45)
  4. print(a)

70. 先分析性能,然后再优化

  • 优化Python程序之前,一定要先分析它的性能,因为导致程序速度缓慢的真正原因未必与我们想的一样。
  • 应该优先考虑用cProfile模块来分析性能,而不要用profile模块,因为前者得到的分析结果更加准确。把需要接受性能测试的主函数传给Profile对象的runcall方法,就可以专门分析出这个体系下面所有函数的调用情况了。
  • 可以通过Stats对象筛选出我们关心的那些分析结果,从而更为专注地思考如何优化程序性能。
  • 分析Python程序的性能之前,一定要提醒自己注意,别把访问外部系统的那些代码,与核心代码混在一起测。如访问磁盘资源等。 ```python from cProfile import Profile from pstats import Stats import time

def sort_p(arr): for i in range(2): time.sleep(1) arr = sorted(arr) return arr

pro = Profile() arr = [1,4,2,6,8,7,2,34,6,8,3,2,75,3] pro.runcall(lambda: sort_p(arr)) pst = Stats(pro) pst.strip_dirs()

查看每个函数消耗的时间

pst.print_stats()

查看每个函数被其他函数调用了多少次

pst.print_callees()

  1. <a name="lxgH1"></a>
  2. # 71. 优先考虑用deque实现生产者-消费者队列
  3. 先进先出的(first-in, first-out,FIFO)队列,这种队列也叫作生产者-消费者队列(producer–consumer queue)或生产-消费队列,其实就是我们常说的队列。通常我们在python中可以用list来实现先进先出,但是list随着元素数量的增加性能会下降。timeit这个库可以用来测试重复运行函数需要花费的时间。<br />使用collections中的deque类实现队列比直接使用list要快。
  4. <a name="h9AL5"></a>
  5. # 72. 考虑用bisect搜索已排序的序列
  6. bisect可以实现快速的二分查找,并且可以考虑用left,right等方法来考虑小于,小于等于这些情况
  7. <a name="KcP4Z"></a>
  8. # 73. 学会使用heapq制作优先级队列
  9. 在对队列中的元素按照某一属性进行排序,比如借书还书的情况,不一定是先借的排在前面,不同的书可借的时间是不同的,因此可能存在后借的书需要先还。这时,需要按照还书时间进行排序。如果采用list那效率会很低,可以采用heapq模块,利用堆结构来维护队列中的元素。<br />加入到heapq中的元素必须是可比较的,因此在自己实现类的时候,需要给类加装饰器@functools.total_ordering,并且在类中实现__lt__方法,确定通过比较哪些属性来维持先后顺序。
  10. <a name="hdo3Q"></a>
  11. # 74. 考虑用memoryview与bytearray来实现无须拷贝的bytes操作
  12. 针对CPU密集型的计算任务,要想用Python程序平行地处理,可能得多花一点功夫(并行),但针对I/O密集型的任务,却很容易就能用各种方式写出吞吐量较大(多线程)的平行代码。**然而,由于这种代码写起来很简单,特别容易遭到误用,让人以为Python好像慢得连I/O密集型任务都处理不好**。<br />memoryview提供了一套无需拷贝的操作,比如通常list做切片的时候是需要在底层做拷贝,如果使用memoryview可以节约时间,在数据量大访问多的时候比较显著。<br />memoryview是对bytes进行封装,所以可以考虑用bytes进行存储,因为,如果是使用字符串,那么字符串转换成bytes再用memoryview的时间还不如直接对字符串进行切片。当然如果转换一次就行,然后后续有多次切片,那也是能节省时间的。<br />如果能把bytes改成Python内置的bytearray,那么与memoryview相搭配,效果会比较好。bytes有个限制,就是只能读取不能修改,我们不能单独更新其中某个位置上的字节。因此,如果需要对数据进行修改的话,可以使用bytearray
  13. ```python
  14. from cProfile import Profile
  15. import time
  16. start = time.time()
  17. a = 'hello in hao'*10000000
  18. for i in range(1000):
  19. b = a[1:300000]
  20. end = time.time()
  21. print(end - start)
  22. start = time.time()
  23. aa = memoryview(a.encode())
  24. starts = time.time()
  25. for i in range(1000):
  26. aaa = aa[1:300000]
  27. # print(aaa.tobytes().decode())
  28. end = time.time()
  29. print(end - start)
  30. print(end - starts)

image.png