第一章:Python性能优化基石
做量化交易这些年,我见过太多人把Python用成了“慢速语言”。其实不是Python慢,是你没选对工具。今天咱们就从数据结构选型开始,聊聊性能优化的那些坑。
1.1 数据结构选型:选对了,性能翻倍
我个人习惯,写任何代码前先想清楚数据长什么样。量化交易里,数据无非几种:时间序列、订单簿、持仓信息。每种数据都有最适合它的容器。
| 数据结构 | 适用场景 | 时间复杂度 |
|---|---|---|
| 列表(list) | 有序数据,频繁追加 | O(1)追加,O(n)查找 |
| 字典(dict) | 键值对,快速查找 | O(1)平均查找 |
| 集合(set) | 去重、成员判断 | O(1)平均查找 |
| 元组(tuple) | 不可变数据,哈希键 | O(1)索引 |
举个例子。我在项目中处理过百万级的订单数据,如果用列表存订单ID,每次查找都要遍历——那速度,简直让人崩溃。换成字典后,查找时间从秒级降到了毫秒级。
核心原则:查找用字典,去重用集合,有序用列表,固定用元组。
1.2 列表与字典的性能陷阱
列表和字典,看着简单,用起来全是坑。我踩过的坑,今天一次性说清楚。
列表的陷阱
列表的insert(0, item)操作,时间复杂度是O(n)。为什么?因为插入到头部,所有元素都得往后挪。我曾经在循环里这么干过,处理10万条数据,跑了整整两分钟。
# 错误示范
data = []
for i in range(100000):
data.insert(0, i) # O(n)操作,慢到怀疑人生
# 正确做法
data = []
for i in range(100000):
data.append(i) # O(1)操作
data.reverse() # 最后反转一次,O(n)
避坑指南:我曾经在实时行情处理中,用列表做队列。结果延迟越来越高,最后发现是pop(0)导致的。换成collections.deque后,问题迎刃而解。
字典的陷阱
字典的键必须是可哈希的。什么意思?就是键不能是列表、字典这种可变类型。我见过有人用列表当键,直接报TypeError。
还有个坑:字典的in操作是O(1),但如果你用keys()方法,它会返回一个视图对象,性能没问题。但如果你用list(dict.keys()),那就变成O(n)了。
# 高效写法
if key in my_dict: # O(1)
value = my_dict[key]
# 低效写法
if key in list(my_dict.keys()): # O(n),多此一举
value = my_dict[key]
1.3 内置函数与标准库:Python的隐藏武器
Python内置函数和标准库,说白了就是官方给你写好的高性能代码。你想想看,这些函数底层都是C语言实现的,比你手写的Python循环快一个数量级。
常用内置函数
map():批量处理,比列表推导式快filter():条件过滤,避免手动循环zip():并行迭代,代码更简洁sorted():排序,底层是Timsort算法
# 用map替代循环
prices = [100.5, 101.2, 99.8]
# 低效
new_prices = []
for p in prices:
new_prices.append(p * 1.01)
# 高效
new_prices = list(map(lambda x: x * 1.01, prices))
标准库的威力
我建议你多看看collections、itertools、functools这几个模块。它们能解决很多常见问题。
| 模块 | 常用工具 | 用途 |
|---|---|---|
| collections | deque, Counter, defaultdict | 队列、计数、默认字典 |
| itertools | chain, groupby, product | 迭代器操作、分组、笛卡尔积 |
| functools | lru_cache, partial | 缓存、偏函数 |
个人经验:我在做回测系统时,用functools.lru_cache缓存计算结果,速度提升了3倍。你想想看,同样的计算重复执行,缓存一下多划算。
1.4 知识体系总览
下面这张图,是我整理的本章节知识体系。你可以把它当成一张地图,随时回来看看。
嗯,这张图把本章的核心内容都串起来了。你写代码时,可以对照着看看自己有没有走弯路。
最后说一句:性能优化不是一蹴而就的事。我建议你先写出能跑的代码,再用工具(比如timeit模块)测一下瓶颈在哪。别一开始就想着优化,那样反而容易过度设计。