我从《Python高性能》这本书里学到了什么?

过早的优化是万恶之源. 如果你的代码还没有到需要考虑性能的地步, 那么你应该关闭这篇文章, 继续去迭代你那该死的代码.

每一个程序员的终极梦想, 都一定要包括优化代码在其中. “吟安一个字, 捻断数茎须”, 如同古时的诗人般勤勤恳恳, 不断地去精简代码, 不断地锤炼代码, 古人炼字, 我辈炼码. 而过程中的艰苦与喜悦是只有自己一个人独享的, 也是人生的终极追求.

每一个人都不得不开始从业务代码开始写起, 起初还有认真对待, 尽量提高代码质量, 就是在项目初期, 就已经考虑到了未来一段时间的需求了. 但久而久之厌倦下来, 对代码的要求也开始变得只要“能跑就行”. 确实是这样, 因为业务场景很多都是短效性的项目, 可能你代码写完跑完结果之后, 就不会再有任何维护的时候了. 总是要求代码优化, 吃力不讨好.

我真的是非常讨厌这样的情况, 也深知非常讨厌这样的自己. 我希望能在编写代码的时候顺手就写出优雅高效的代码, 但如果没有一个良好的规范和质量把控, 就一直只能写写如💩一样的代码. 💩山估计也是这样来的吧.

所以抱着这样的兴趣, 我开始读起了这本书《Python高性能》, 我希望自己能在思考的时候顺便多想了一个可能的点, 而写出后续不需要怎么维护就能行的代码, 希望在评估时, 能对自己的程序的上下限有着准确的估计, 能超出代码的可用性去思考代码的极限.

性能剖析器

TODO: 介绍cProfile, line_profiler, memory_profiler

对于Python自身性能的思考

列表

  • list在访问, 修改和附加元素方面为O(1), 但是在列表开头, 中间添加和删除元素因为元素需要后移一位, 因此为O(N)
  • deque能实现在两端高效地添加和删除元素, 它的pop, append, popleft, appendleft方法都是O(1). 但是访问deque中间的元素需要的时间为O(N)
  • 列表中查找元素通常是O(N), 因为这是使用list.index来完成的. 要想提高列表的查找速度, 可以是先确保底层数组有序, 然后用bisect来进行二分查找
    O(logN)

字典

对于频繁的查询, 可以通过在预处理时建立反向索引来节约时间成本. 但是这种反向索引可能是代价高昂的, 因为必须考虑到每个可能的反向查询. 但是反向索引建立完后效果是非常显著的.

集合

集合也是基于散列实现的, 其加法, 删除和成员资格测试都是O(1), 即不受集合规模影响.

堆是一种设计用于快速查找并提取集合中最大值或最小值的数据结构, 其典型用途是按优先级处理一系列任务. 堆的插入操作和提取最大值的操作均为O(logN)

优先级队列

PriorityQueue可以用于提取最大值最小值, 同时它还是线程和进程安全的.

  • 使用PriorityQueue.get来提取最小值
  • 要提取最大的元素, 可以采用一个简单的诀窍——将每个元素都乘以-1, 这将反转元素的排列顺序
  • 可以插入形如(number, object)的元组, 因为元组的比较是根据第一个元素排序的.

字典树

字典树在查找前缀匹配的字符串方面非常高效