写Python的人大多都遇到过这种情况:程序一开始跑得好好的,数据量一上来,直接卡成PPT。其实很多时候不是语言不行,而是写法可以更聪明一点。
别在循环里做重复计算
比如你要处理一个大列表,每次都要用到它的长度,有人习惯这么写:
for i in range(len(data)):
process(data[i])
每次循环都调用 len(data),看着没啥问题,但在大数据下就是隐形拖累。更好的方式是提前算好:
n = len(data)
for i in range(n):
process(data[i])
用生成器代替列表存储
当你处理大量数据但不需要一次性全装进内存时,生成器就是救星。比如读一个几GB的日志文件,用列表会直接爆内存:
def read_logs_bad(filename):
return [line.strip() for line in open(filename)]
# 改成生成器,一行一行来
def read_logs_good(filename):
with open(filename) as f:
for line in f:
yield line.strip()
看起来只是return变yield,但内存占用可能从几个G降到几十M。
善用内置函数和库
像 sum、map、filter 这些C实现的函数,通常比自己写for快不少。比如求平方和:
# 慢
result = 0
for x in nums:
result += x * x
# 快
result = sum(x * x for x in nums)
别小看这点差异,数据量一大就明显了。
字符串拼接别用+串联
很多人习惯用 + 拼接大量字符串,比如日志输出或生成HTML。但Python中字符串不可变,每次+都会创建新对象。
# 不推荐
s = ""
for item in items:
s += str(item)
# 推荐
s = ''.join(map(str, items))
尤其是拼接上千条记录时,速度差好几倍。
考虑用PyPy或Cython
如果你的项目允许换解释器,PyPy对纯Python代码的加速效果经常让人惊喜。某些场景下提速5倍都不稀奇。而Cython适合把关键计算模块编译成C,特别适合数学运算密集型任务。
少用点点点,多用局部变量
Python查找局部变量比全局或属性快。在一个高频循环里,把频繁访问的对象方法提出来:
# 慢
for item in data:
sys.stdout.write(item)
# 快
write = sys.stdout.write
for item in data:
write(item)
看起来抠门,但在每秒处理上万条数据的服务里,这种细节真能省下CPU。
性能优化不是非得重写整个系统,往往改几行代码就能见效。关键是意识到哪些操作代价高,然后绕开它们。多用timeit测,别靠猜。