编程学习网 > 编程语言 > Python > Python性能优化三招:代码提速几十倍,但90%的人都用错了方法!
2026
10-08

Python性能优化三招:代码提速几十倍,但90%的人都用错了方法!


写Python代码的人常碰到一个情况。程序跑得慢。自己觉得逻辑没问题。电脑风扇转得厉害。这时候需要看看代码哪里出了问题。优化不是把代码改短。也不是用上各种花哨写法。优化是让程序干活更少。让CPU做真正有用的事。

先看一个例子。假设要处理一千万个数字。找出其中所有偶数。很多人会写一个循环。往空列表里不停添加。代码长这样。

result = [] for i in range(10000000):     if i % 2 == 0:         result.append(i)

这段代码能跑。速度不快。原因是每次循环都调用append方法。append本身是C实现的。调用次数太多。开销就上来了。更好的写法用列表推导式。

result = [i for i in range(10000000) if i % 2 == 0]

列表推导式在解释器层做了优化。中间没有Python函数调用的开销。速度能快两倍左右。这个差距在数据量大的时候非常明显。

再看一个常见操作。字符串拼接。初学者会这样做。

s = '' for word in words:     s += word

字符串在Python里不可变。每次加号都创建一个新字符串。旧字符串被扔掉。一千万次操作就创建一千万个临时对象。内存分配和回收吃掉大量时间。换成join方法。

s = ''.join(words)

join只分配一次内存。把所有片段一次性拷贝进去。速度快几十倍。这个写法的另一个好处是代码更短。可读性反而更好。

循环内部还有优化空间。看下面这段代码。

for i in range(len(data)):     process(data[i])

每次循环都要算len(data)。len在Python里很快。毕竟它是内置函数。更好的做法是用enumerate或者直接迭代。

for item in data:     process(item)

直接迭代省掉了索引操作。也省掉了len调用。代码更干净。有人担心拿不到索引。enumerate就是为这个场景设计的。

for index, item in enumerate(data):     process(index, item)

字典的遍历也有讲究。不需要键的时候用values。不需要值的时候用keys。两个都需要用items。有人习惯用keys再取value。那样多做一次哈希查找。

for key in my_dict:     value = my_dict[key]

改成下面这样少一次查找。

for key, value in my_dict.items():     pass

函数调用也有成本。Python的函数调用比C慢很多。如果一个函数在循环里被调用几百万次。开销就不可忽视。有时候可以把函数体直接展开到循环里。代价是代码变长。需要看是否值得。

局部变量比全局变量快。Python访问局部变量用的是数组索引。访问全局变量用的是字典查找。规则是LEGB。局部、闭包、全局、内置。写循环的时候把全局变量赋给一个局部名字。

def compute():     local_data = global_data     for i in range(1000000):         local_data[i] = i * 2

这样每次循环省掉一次全局查找。积少成多。效果在热循环里很明显。

生成器适合处理大数据。列表会把所有结果放进内存。生成器一次只产出一个。处理日志文件或者数据库查询结果时。生成器能省下大量内存。

def read_lines(file_path):     with open(file_path) as f:         for line in f:             yield line.strip()

用这个函数的时候不会一次性读入整个文件。每读一行处理一行。内存占用恒定。适合处理几十G的日志。

内置函数和标准库是用C写的。速度比纯Python快一到两个数量级。能用内置就用内置。比如求和一个列表。自己写循环累加。不如直接用sum。

total = sum(numbers)
类似的还有min、max、any、all。这些函数不仅快。还更不容易写错。

选择合适的数据结构也能提速。判断一个元素是否在集合里。用set比用list快。list是线性查找。set是哈希查找。当元素数量超过几千。差距非常明显。

members = set(ids) if target in members:     pass

操作字符串的时候注意编码。频繁做encode和decode会消耗CPU。如果整个流程都是字节操作。那就一直用bytes。不要来回转换。

多线程在Python里有GIL的限制。CPU密集的任务用多线程不会变快。这时候用多进程。multiprocessing模块能利用多核。IO密集的任务多线程还是有效的。因为线程在等待IO时会释放GIL。

异步IO适合网络请求。aiohttp加asyncio能处理成千上万的并发连接。每个连接不会独占一个线程。资源消耗比多线程小很多。写异步代码要注意不要在协程里做同步阻塞操作。一个阻塞调用会卡住整个事件循环。

代码性能分析不能靠猜。用cProfile找出真正的瓶颈。

import cProfile cProfile.run('my_function()')

它会输出每个函数的调用次数和耗时。看到输出结果往往跟直觉不一样。花时间优化一个只占1%时间的函数没有意义。把精力放在占80%时间的那部分。

line_profiler能分析到每一行。安装之后用kernprof运行。看哪一行最慢。然后针对那一行改。

算法上的改进永远比代码微调重要。一个O(n²)的算法换成O(n log n)。比任何语法优化都管用。写代码之前先想清楚数据规模和操作复杂度。数据小的时候随便写。数据大了必须选对算法。

用PyPy解释器可以不用改代码就提速。PyPy带JIT编译器。对长期运行的程序效果很好。对短脚本效果不明显。因为JIT需要时间预热。CPython也在改进。3.11版本比3.10快了不少。升级解释器版本是最省力的优化方式。

用type hints不会让代码变快。它只是给人和工具看的。不过配合mypy能提前发现类型错误。减少运行时的意外。mypy检查不花运行时间。值得加上。

最后提一点。优化之前先保证代码正确。没有测试的优化是危险的。改完跑一遍测试用例。确认结果跟原来一样。性能测试要跑多次取平均值。单次测量波动太大。用timeit模块做基准测试比较可靠。

优化是一个权衡。代码可读性、开发时间、运行速度三者要平衡。不要为了快把代码写成天书。三个月后自己都看不懂。先写清楚。再测性能。最后只改真正慢的地方。

以上就是“Python性能优化三招:代码提速几十倍,但90%的人都用错了方法!”的详细内容,想要了解更多Python教程欢迎持续关注编程学习网。  

扫码二维码 获取免费视频学习资料

Python编程学习

查 看2022高级编程视频教程免费获取