“Python 不慢,是你没写对。” —— 资深 Python 工程师的忠告
Python 以简洁和易读著称,但也常被诟病“执行慢”。然而,真正的瓶颈往往不在语言本身,而在开发者对底层机制的理解不足。
本文基于 JetBrains PyCharm 团队最新发布的《10 Smart Performance Hacks For Faster Python Code》一文,结合实战代码与性能实测数据,为你揭示 10 个简单却高效的 Python 性能优化技巧。每一个技巧都能在不牺牲代码可读性的前提下,带来 数倍甚至百倍的性能提升。
技巧 1:用 set 替代 list 做成员检查
问题:在大列表中用 x in list 检查元素存在性,时间复杂度为 O(n)。
解决方案:改用 set,平均时间复杂度为 O(1)。
big_list = list(range(1_000_000))
big_set = set(big_list)
# 测试
999_999 in big_list # ≈0.015 秒
999_999 in big_set # ≈0.00002 秒(快 750 倍!)
✅ 适用场景:去重、白名单校验、ID 存在性判断。
技巧 2:避免不必要的对象拷贝
问题:频繁调用 list.copy()、dict.copy() 会触发内存分配与数据复制。
解决方案:尽量原地修改(in-place),减少中间副本。
def modify_in_place(lst):
lst[0] = 999
return lst # 无拷贝
def copy_and_modify(lst):
new_lst = lst.copy() # 昂贵操作
new_lst[0] = 999
return new_lst
实测:处理百万级列表时,原地修改快 100 倍。
技巧 3:对高频类使用 __slots__
问题:Python 默认用 __dict__ 存储实例属性,内存开销大。
解决方案:用 __slots__ 固定属性,节省内存并加速访问。
class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
实测:创建 100 万个 Point 实例,内存减少 40%,初始化快 20%。
技巧 4:用 math 模块替代运算符
问题:x ** 0.5 虽简洁,但比 math.sqrt(x) 慢且精度低。
解决方案:数值计算优先使用 math 模块(C 实现)。
import math
numbers = range(10_000_000)
# 快且准
[math.sqrt(n) for n in numbers] # ≈0.20 秒
# 慢且可能有浮点误差
[n ** 0.5 for n in numbers] # ≈0.25 秒
✅ PyCharm 提示:输入 math. 自动弹出函数列表,提升编码效率。
技巧 5:预分配已知大小的列表
问题:list.append() 在内部会动态扩容,触发内存拷贝。
解决方案:若已知最终长度,预先分配。
# 慢(动态扩容)
result = []
for i in range(1_000_000):
result.append(i)
# 快(预分配)
result = [0] * 1_000_000
for i in range(1_000_000):
result[i] = i
实测:预分配快 30%,且内存布局更连续,利于缓存。
技巧 6:避免在热循环中使用异常控制流
问题:异常处理涉及栈展开,开销极大。
错误示范:
for i in numbers:
try:
total += i / (i % 2)
except ZeroDivisionError:
total += i
正确做法:
for i in numbers:
if i % 2 != 0:
total += i // 2
else:
total += i
实测:条件判断比异常快 2 倍,且逻辑更清晰。
技巧 7:将重复逻辑封装为局部函数
原理:局部作用域变量查找比全局快。
def process_data():
def add(a, b): # 局部函数
return a + b
total = 0
for i in range(10_000_000):
total = add(total, i) # 更快
return total
实测:局部函数调用快 10%~15%,尤其在高频循环中。
💡 PyCharm AI 助手:选中代码 → 点击灯泡 → “Suggest Refactoring”,自动推荐优化方案。
技巧 8:用 itertools 处理组合问题
问题:手动写嵌套循环生成笛卡尔积,效率低且占内存。
解决方案:使用 itertools.product()。
from itertools import product
items = [1, 2, 3] * 10
# 高效、懒加载、省内存
list(product(items, repeat=2)) # ≈0.0005 秒
# 手动循环
[(x, y) for x in items for y in items] # ≈0.002 秒(慢 4 倍)
✅ 优势:延迟计算、内存友好、C 优化。
技巧 9:用 bisect 维护有序列表
问题:在有序列表中插入新元素后手动排序,O(n log n)。
解决方案:bisect.insort() 用二分查找插入,O(log n)。
import bisect
sorted_list = list(range(0, 1_000_000, 2))
bisect.insort(sorted_list, 75432) # ≈0.0001 秒
# 手动查找插入位
for i, v in enumerate(sorted_list):
if v > 75432:
sorted_list.insert(i, 75432)
break # ≈0.01 秒(慢 100 倍!)
技巧 10:缓存循环中的重复函数调用
问题:在循环内反复调用无副作用的函数,浪费算力。
解决方案:提前计算,缓存结果。
def expensive():
time.sleep(0.001) # 模拟耗时操作
return 42
# 错误:调用 1000 次
for _ in range(1000):
result += expensive() # 耗时 ≈1 秒
# 正确:调用 1 次
value = expensive()
for _ in range(1000):
result += value # 耗时 ≈0.001 秒
总结:性能优化的核心原则
| 原则 | 说明 |
|---|---|
| 用对数据结构 | set > list(查)、deque > list(头尾操作) |
| 减少内存分配 | 预分配、原地修改、避免拷贝 |
| 利用 C 扩展 | math、itertools、bisect、collections |
| 避免“昂贵”操作 | 异常、全局变量、重复计算 |
| 善用工具 | PyCharm AI Assistant、cProfile、line_profiler |
记住:优化不是重写,而是 精准手术。
先用性能分析工具定位瓶颈,再用上述技巧“点穴式”提升。