大家好,欢迎来到 Crossin 的编程教室。
作为每天都在用 Python 工作的开发者,我最常听到大家的吐槽就是:“Python 写起来是真爽,但跑起来也是真慢!”
尤其是在处理一些大量计算或者跑数据的时候,看着控制台半天不跳下一个进度条,恨不得进去推它一把。
其实,很多时候并不是 Python 不行,而是我们使用的“姿势”不对。今天 Crossin 就来跟大家分享 4 个实战中非常管用的提速技巧,直接上代码!
1. 别再死磕 for 循环了,用 NumPy 降维打击
这是很多刚进阶的同学最容易踩的坑:处理一大堆数据时,下意识地就写个 for 循环遍历。
Python 解释器跑这种原生的 for 循环开销极大。遇到数值计算,能用 NumPy 向量化解决的,绝不用 for 循环。 NumPy 底层是 C 语言,它能一次性把操作打在整个数组上。
标准写法:
import timedef slow_sum(n): a = list(range(n)) b = list(range(n)) c = [] for i in range(len(a)): c.append(a[i] + b[i]) return c# 跑 1000 万次,测测看start = time.time()slow_sum(10**7)print(f"原生循环耗时: {time.time() - start:.2f}s")
加速写法:
import timeimport numpy as npdef fast_sum(n): a = np.arange(n) b = np.arange(n) return a + bstart = time.time()fast_sum(10**7)print(f"NumPy 向量化耗时: {time.time() - start:.4f}s")
运行结果:
原生循环耗时:1.82s
NumPy 向量化耗时:0.12s
千万级数据下,NumPy 比原生列表循环快了15倍。
2. 实在绕不开循环?加个 @njit 装饰器救命
有些复杂的业务逻辑,比如带有复杂判断条件的数值模拟,真的没法直接用 NumPy 拼出来,必须得写 for 循环怎么办?
这时候,祭出神器 Numba。它是一个 JIT(即时编译器),作用非常粗暴:把你的 Python 函数直接翻译成机器码。
用法超级简单,只需要引入库,并在函数头上加一行 @njit。
标准写法:
import timedef check_logic(n): res = 0 for i in range(n): if i % 3 == 0: res += i return resstart = time.time()check_logic(10**8)print(f"原生 Python 耗时: {time.time() - start:.2f}s")
加速写法:
import timefrom numba import njit@njit # 只需要这一个装饰器def check_logic_fast(n): res = 0 for i in range(n): if i % 3 == 0: res += i return res# 第一次运行会有编译时间,之后起飞start = time.time()check_logic_fast(10**8)print(f"Numba JIT 耗时: {time.time() - start:.2f}s")
运行结果:
原生 Python 耗时: 6.54s
Numba JIT 耗时: 0.42s
Numba 第一次运行时要编译,优化不明显,之后的运行速度大约提升了15倍。
3. 多核 CPU 别闲着,多进程 multiprocessing 跑起来
众所周知,Python 有个叫 GIL(全局解释器锁)的玩意儿,导致单进程下多线程没法同时利用多核 CPU。
如果你需要处理比如:大规模图片压缩、海量文件解析这种“吃 CPU”(计算密集型)的任务,别犹豫,直接上多进程,强行把你的 8 核、10 核 CPU 榨干。
标准写法:
import timedeftask(n):returnsum(i * i for i inrange(n))start = time.time()[task(10**7) for _ inrange(4)]print(f"单进程顺序执行: {time.time() - start:.2f}s")
加速写法:
import timeimport multiprocessingdeftask(n):returnsum(i * i for i inrange(n))if __name__ == "__main__": start = time.time()with multiprocessing.Pool(processes=4) as pool: pool.map(task, [10**7] * 4)print(f"4进程并行执行: {time.time() - start:.2f}s")
运行结果:
单进程顺序执行: 3.98s
4进程并行执行: 1.23s
千万级数据下,4进程并行比单进程快了3倍多。
4. 终极偷懒大招:代码一行不改,换 PyPy 跑
如果你上面这些都不想搞,或者这是一个纯粹用 Python 写的庞大项目,有没有“物理外挂”?
有的,直接把执行命令里的 python 换成 pypy。
PyPy 是自带了 JIT 编译器的 Python 替代品。你不需要改动任何一行代码。
举个例子,一个算斐波那契数列的脚本 fib.py:
# fib.pydef fib(n): if n < 2: return n return fib(n-1) + fib(n-2)import timestart = time.time()fib(36)print(f"执行耗时: {time.time() - start:.2f}s")
你平时这么跑:python fib.py
执行耗时:3.16s
现在这么跑:pypy3 fib.py
执行耗时:0.21s
纯 Python 代码的逻辑,换个“发动机”,就获得了15倍的速度提升。不过要注意,pypy3运行环境需要额外安装,并且如果你的项目重度依赖某些特定的 C 扩展库,兼容性需要提前测试一下。
总结:
写 Python 遇到性能瓶颈了,可以参考这个思路:
玩数据矩阵:优先想办法用 NumPy。
死磕计算循环:用 Numba 加速试试。
批量粗活累活:开 多进程 一起上。
祖传老代码:丢给 PyPy 碰碰运气。
但也要注意,优化是有成本的,别为了快而快,先把功能实现好。否则过早的优化是万恶之源!
大家平时在写代码时还踩过哪些性能的坑?或者有什么压箱底的优化绝活?欢迎在评论区留言聊聊。
如果本文对你有帮助,欢迎点赞、评论、转发。你们的支持是我更新的动力~
添加微信 crossin123 ,加入编程教室共同学习~