当前位置:首页>python>Python跑了一夜没出结果?换个写法快了100倍

Python跑了一夜没出结果?换个写法快了100倍

  • 2026-09-08 20:59:30
Python跑了一夜没出结果?换个写法快了100倍

上周三晚上,我跑一个联邦学习的实验代码,数据集不大,大概50万条。点了运行,去睡觉。

第二天早上起来看,还没跑完。进度条卡在37%,估算剩余时间:11小时。

同门的师兄看了一眼我的代码,改了3行,重新跑。5分钟出结果。

当时我觉得自己像个傻子。但后来发现,这不是我一个人的问题——大部分科研人写Python,从来不考虑性能。代码能跑就行,直到跑不动了才慌。

这篇文章把我踩过的所有Python性能坑总结出来。不需要你是计算机专业的,跟着改就行。

一、为什么你的Python这么慢?

先说结论:90%的慢,不是Python本身的问题,是写法的问题。

Python慢的根源通常就3个:

  1. 用了for循环处理大数据
    (应该用向量化操作)
  2. 频繁创建和销毁对象
    (内存碎片化)
  3. 没利用多核CPU
    (Python默认单线程跑)

下面逐个解决。

二、第1个坑:for循环是性能杀手

错误写法(慢100倍)

假设你要对50万条数据做平方运算。大部分人的写法:

import numpy as np  data = np.random.rand(500000) result = [] for x in data:     result.append(x ** 2) result = np.array(result)

这个写法在我的机器上跑:3.2秒。看起来不慢?等数据量到500万就变成32秒,到5000万就要5分钟。

正确写法(向量化)

result = data ** 2  # 就这一行

同样的操作:0.008秒。快了400倍。

原理很简单:for循环是逐个元素处理,每次都要Python解释器介入。而NumPy的向量化操作底层是C语言实现的,一次性处理整个数组,跳过了Python解释器的开销。

记住一条铁律:在NumPy/Pandas里看到for循环,先想想能不能换成向量化操作。90%的情况下可以。

三、第2个坑:DataFrame的iterrows陷阱

处理表格数据时,很多人喜欢用Pandas的iterrows逐行处理:

# 慢写法:逐行处理 for index, row in df.iterrows():     if row['score'] > 80:         df.loc[index, 'grade'] = 'A'     else:         df.loc[index, 'grade'] = 'B'

10万行数据,这个写法要跑15秒。

正确写法:用apply或np.where

# 快写法1:np.where import numpy as np df['grade'] = np.where(df['score'] > 80, 'A', 'B')  # 快写法2:apply(复杂逻辑用这个) df['grade'] = df['score'].apply(lambda x: 'A' if x > 80 else 'B')

同样的10万行:0.03秒。快了500倍。

如果逻辑更复杂(需要多列参与计算),用np.select:

conditions = [     df['score'] > 90,     df['score'] > 80,     df['score'] > 60 ] choices = ['A', 'B', 'C'] df['grade'] = np.select(conditions, choices, default='F')

一行搞定多条件判断,比iterrows快几百倍。

四、第3个坑:重复计算

这个坑很隐蔽,但极其常见。看这段代码:

# 慢写法:每次循环都重新计算 for col in columns:     normalized = (df[col] - df[col].mean()) / df[col].std()     df[col + '_norm'] = normalized

看起来没毛病,但如果columns有100列,每次循环Pandas都要重新扫描整个列做统计。可以改成一次性计算:

# 快写法:向量化标准化 means = df[columns].mean() stds = df[columns].std() df_norm = (df[columns] - means) / stds # 一次性加后缀 for col in columns:     df[col + '_norm'] = df_norm[col]

100列数据,从12秒降到0.2秒。

五、第4个坑:没用多进程

Python有GIL(全局解释器锁),默认只能用一个CPU核心。你的电脑8核16线程,Python只用了1核,其余15个在围观。

如果你要跑参数搜索(比如不同超参数组合训练模型),用multiprocessing:

from multiprocessing import Pool  def train_with_params(params):     # 你的训练逻辑     model = train_model(params)     return model.score()  if __name__ == '__main__':     param_list = generate_param_grid()  # 100组参数     with Pool(processes=8) as pool:      # 用8个核         results = pool.map(train_with_params, param_list)

100组参数,单核跑10小时,8核并行1.5小时搞定。

注意:多进程适合CPU密集型任务(模型训练、数值计算)。如果是IO密集型(读文件、网络请求),用多线程或asyncio就行。

六、一张图总结:Python性能优化优先级

优先级
优化手段
提速倍数
难度
1
for循环 → 向量化
100-400x
低
2
iterrows → np.where/apply
100-500x
低
3
避免重复计算
10-60x
中
4
多进程并行
≈CPU核心数
中
5
Numba JIT编译
10-100x
高

前3个改完,90%的性能问题都解决了。Numba是进阶玩法,适合数值计算密集的代码——在函数上加一个@jit装饰器,Python直接编译成机器码执行,速度接近C语言。

七、常见坑补充

坑1:用list存数据而不是numpy array

Python list的每个元素都是独立对象,内存占用是numpy array的10-20倍,操作速度也慢得多。数值数据一律用numpy array。

坑2:Pandas的inplace=True不一定快

很多人觉得inplace=True更省内存,但实际上Pandas底层有时会先创建副本再修改。不要依赖inplace,改用链式赋值更清晰。

坑3:读取大文件用read_csv全量加载

文件超过1GB时,read_csv会卡死。解决方案:用chunksize参数分块读取,或换用Dask(类似Pandas但支持并行和懒加载)。

写在最后

科研代码不需要写得多优雅,但"能跑"和"能跑完"是两回事。我见过太多人因为代码太慢,实验跑了一周才出一个结果,而别人一下午跑了20组实验。

性能优化的投入产出比极高:花1小时改写法,省下来的可能是几百小时的等待时间。

关注后回复【Python性能】,领取:

  • 本文所有代码的完整可运行版本(Jupyter Notebook)
  • NumPy/Pandas向量化操作速查表(一张图覆盖90%常用场景)
  • multiprocessing参数搜索模板代码(直接套用)

你跑Python最慢的一次等了多久?评论区吐槽,点赞最高的送一份完整性能优化手册。

这是「科研人效率工具系列」第2期,上期:导师催交论文,我用Zotero半天整理了200篇文献。下期预告:用AI 10分钟读完100篇文献(真的能做到)

— 关注「科研创新社」,每天一个科研效率提升技巧 —

最新文章

随机文章