Python执行速度慢的原因及全面优化方案
前言「Python 慢」是一个流传很广的结论但很多人对它只有感觉、没有理解。常见的误解有两类一类是把它归因于「解释器写得差」另一类是以为「多开几个线程就快了」。这两个说法都不准确。准确的说法是Python这里指 CPython 实现的慢主要来自它的执行模型——先编译成字节码再由虚拟机逐条解释执行同时它是动态类型语言每次操作都要在运行时确定对象的类型。这两点决定了它把很多工作留到了运行时。这跟「写得差」不是一回事它是一种设计取舍换来的是灵活和开发效率。本文会分三层讲先讲执行模型为什么导致额外开销再讲 GIL全局解释器锁划定了并发的边界最后给出一套按层面组织的优化方案。全文不会给出任何未经测量的性能倍数——具体到你的场景快多少请用timeit自己测。需要声明本机没有 Python 解释器本文代码均无法在本环境实际执行都是按 Python 官方文档整理的写法请以官方文档为准。本文不声称任何代码被运行过也不会给出「实测结果如下」之类的表述。一、执行模型解释执行加动态类型解释执行CPython 先把源码编译成字节码bytecode然后由一个循环常被称作「求值循环」evaluation loop逐条取字节码、分派、执行。对比之下编译型语言在编译期就把很多东西确定下来运行时几乎没有这层「取指令—判断指令—跳转执行」的开销。Python 每条字节码都要走一遍这层分派累加起来就是可观的开销。动态类型Python 的变量没有类型值对象才有类型。a b这一句解释器在运行时才去看a是什么、b是什么、它们的__add__该怎么走。这就是「类型分派」。同时Python 里连整数都是对象它有一个头部装着引用计数、类型指针。你做一个整数运算往往不只是做一次机器加法还牵涉到对象的创建、引用计数的增减——这就是常说的「装箱」boxing成本。小的整数在 CPython 里会被缓存复用大致是 -5 到 256 这个区间但这是实现细节不要依赖。需要特别提醒以上是「机制解释」不要把它换算成「Python 比 C 慢多少倍」。那种数字要看具体任务、具体实现本文不给任何没出处的倍数都不可信。引用计数与内存CPython 用引用计数为主来管理内存配合分代垃圾回收处理循环引用。引用计数的增减是常量时间操作但每次对象赋值、传参都会触发这也是执行模型的一部分成本。二、GIL 与并发的边界GIL 是「全局解释器锁」。它的作用是在同一个进程内同一时刻只有一个线程在执行 Python 字节码。这带来两个直接结论对 CPU 密集任务多线程不会带来并行加速。因为同一时刻只有一个线程在跑字节码多开线程只是轮流执行还要付出切换成本。对 I/O 密集任务多线程是有效的。线程在等待 I/O读写文件、网络请求时会释放 GIL别的线程可以趁机执行。所以网络爬取、文件批处理这类以等待为主的任务多线程能提高吞吐。CPU 密集任务的正确路径是多进程multiprocessing每个进程有独立的解释器和独立的 GIL可以真正并行或者把热点下沉到 C 扩展、原生扩展去实现。关于「Python 已经没有 GIL 了」这个说法不准确。CPython 从 3.13 起提供了实验性的自由线程free-threaded可禁用 GIL构建对应 PEP 703但官方明确说明它是实验性的、默认不启用需要用单独的构建可执行文件名通常带t后缀来运行。默认构建依然有 GIL。任务类型多线程多进程异步 I/O等待网络 / 文件有效可用但偏重有效大量纯计算无明显加速有效能并行不适用混合型视比例而定视比例而定视比例而定三、优化方案按层面下手优化的第一原则是先测量再优化用标准库的timeit模块量出瓶颈在哪而不是凭感觉改。# 适用于 Python 3.8import timeit# timeit(stmt, setup, number)把 stmt 执行 number 次返回总秒数setup data list(range(10000))stmt sum(data)print(timeit.timeit(stmt, setupsetup, number1000))需要比较不同重复次数下的稳定性时用timeit.repeat它返回一串结果可以看波动。1. 算法与数据结构先问数据结构选对了没有。成员判断用列表是线性扫描用集合或字典是哈希查找在数据量大时通常更划算# 适用于 Python 3.8data [i * 2 for i in range(10000)]target 9998# 列表逐个比较in_list target in data# 集合哈希查找seen set(data)in_set target in seenprint(in_list, in_set)两种写法结果相同但复杂度不同。具体快多少取决于数据规模和分布请自己用timeit测。2. 用内置函数和 C 实现替代手写循环sum、max、min、any、all、sorted这些内置函数以及str.join、map、filter底层是用 C 实现的它们把循环藏在 C 里跑省掉了大量字节码分派。# 适用于 Python 3.8nums [3, 1, 4, 1, 5]# 手写循环total 0for n in nums:total n# 内置实现total2 sum(nums)print(total, total2)字符串拼接尤其要注意反复拼接会不断产生新字符串对象而join一次性算出结果。# 适用于 Python 3.8parts [str(i) for i in range(1000)]# 逐次拼接s1 for p in parts:s1 p# 一次性拼接s2 .join(parts)print(len(s1) len(s2))3. 减少重复计算把循环里不变的计算提到循环外是收益最直接的一类优化。函数调用同样如此在循环里反复调re.compile去编译同一个正则就是典型的重复计算。# 适用于 Python 3.8import relines [a1, b22, c333]pattern re.compile(r\d) # 编译一次for line in lines:m pattern.search(line)# 说明re 模块内部会缓存最近编译过的模式# 但显式编译一次再复用语义更清楚也免去缓存查找。用functools.lru_cache做记忆化可以避免对相同参数重复计算# 适用于 Python 3.8from functools import lru_cachelru_cache(maxsizeNone)def fib(n):return n if n 2 else fib(n - 1) fib(n - 2)print(fib(30))lru_cache的参数签名是lru_cache(maxsize128, typedFalse)也可以直接当无参装饰器用在函数上。它的缓存是线程安全的。适合的情况是「相同参数会被反复调用」如果参数每次都不同缓存只会白占内存。4. 局部变量绑定函数里的局部变量访问走的是「按索引取」这条较快路径全局名和属性访问则要多几步查找。把频繁使用的全局名比如某个模块里的函数绑定成局部名可以减少这部分开销。# 适用于 Python 3.8import mathdef calc(values):sqrt math.sqrt # 绑定为局部名return [sqrt(v) for v in values]print(calc([1, 4, 9]))5. 生成器替代一次性大列表列表会把所有元素一次性放进内存生成器则按需产出。当数据只需遍历一次、且体量很大时生成器能显著降低内存峰值。# 适用于 Python 3.8def count_up(n):i 0while i n:yield ii 1total sum(count_up(5)) # 逐个产出不构造整张列表print(total)注意生成器只能遍历一次遍历完就空了。6.__slots__减少每实例开销默认情况下每个实例都带一个__dict__来存属性。用__slots__显式声明属性名后实例不再需要那个字典占用更小属性访问路径也更直接。# 适用于 Python 3.8class Point:__slots__ (x, y)def __init__(self, x, y):self.x xself.y yp Point(1, 2)print(p.x, p.y)代价是不能再给实例动态添加未声明的属性且继承自没有__slots__的类时效果会被削弱。7.array与memoryview需要大量同类型数值时array模块提供更紧凑的存储memoryview允许在不复制底层缓冲区的前提下按视图访问数据。# 适用于 Python 3.8from array import arraynums array(i, range(10)) # 类型码 i 表示有符号整数print(len(nums), nums[3])mv memoryview(bhello)print(mv[0]) # 直接读底层字节不复制常见坑点1. 不做测量就动手优化。❌ 凭直觉改代码改完发现瓶颈根本不在那。✅ 先用timeit量出热点再针对热点改。2. 以为多线程能让计算变快。❌ 给一个纯计算的循环套上多线程期望加速。✅ 受 GIL 限制CPU 密集要用多进程或原生扩展I/O 密集才适合多线程。3. 说「Python 已经没有 GIL 了」。❌ 把 3.13 的实验性自由线程构建当成默认能力。✅ 它默认不启用需单独构建默认解释器仍有 GIL。4. 在循环里反复re.compile。❌ 每次迭代都编译一遍同一个正则。✅ 编译一次、循环外复用。5. 用在循环里拼大量字符串。❌ 逐次拼接反复产生新字符串对象。✅ 收集到列表里用str.join一次性拼。6. 用列表做大量成员判断。❌ 对一个大列表反复做x in lst。✅ 改用具哈希查找的集合或字典。7. 给参数每次都不同的函数加缓存。❌ 无脑上lru_cache缓存全不命中只白占内存。✅ 只在「相同参数会被反复调用」时使用。8. 忽视生成器只能遍历一次。❌ 把一个生成器遍历两遍第二遍得到空结果。✅ 需要多次遍历就物化成列表或每次重新创建生成器。总结层面手段原理执行模型理解解释执行与动态类型每次操作有分派与装箱开销并发多进程 / I/O 用线程GIL 限定了线程内的并行数据结构列表换集合 / 字典哈希查找替代线性扫描循环用内置函数与join把循环下沉到 C 实现重复计算提到循环外、lru_cache同样的结果不重复算内存生成器、__slots__、array减少对象与字典开销「Python 慢」的根源在执行模型和动态类型而不是「解释器写得烂」优化的顺序永远是先测量、再动手选对数据结构往往比微调写法更有效并发要认清 GIL 的边界CPU 密集用多进程I/O 密集才用线程。至于能快多少请在你的数据上用timeit自己得出结论。

相关新闻

Shodan、FOFA、Censys 网络空间搜索:Legendary OSINT 基础设施侦察完整指南

Shodan、FOFA、Censys 网络空间搜索:Legendary OSINT 基础设施侦察完整指南

Shodan、FOFA、Censys 网络空间搜索:Legendary OSINT 基础设施侦察完整指南 【免费下载链接】Legendary_OSINT A list of OSINT tools & resources for (fraud-)investigators, CTI-analysts, KYC, AML and more. 项目地址: https://gitcode.com/GitHub_Tren…

2026/10/11 10:08:01 阅读更多 →
AI芯片软硬件协同设计:从编译器到算子的关键细节

AI芯片软硬件协同设计:从编译器到算子的关键细节

前两篇我们聊了AI芯片的整体架构选型和指令集设计的思路,这一篇我打算把视角往下压一层,着重聊聊软硬件到底是怎么“咬合”在一起的。很多人对AI芯片有个误解,觉得硬件做出来、编译器一接、算子库一填,就能跑模型。实际做过一个完…

2026/10/11 10:08:01 阅读更多 →
前端转AI实战:用SQLite构建轻量级Agent记忆库

前端转AI实战:用SQLite构建轻量级Agent记忆库

1. 项目概述:为什么一个前端工程师要在第16天突然扎进数据库?“前端转 AI 100 天”这个标题本身就很说明问题——它不是一份学院派学习路线图,而是一个真实从业者在职业转型十字路口的每日实录。Day 16 这个时间点特别值得琢磨:前…

2026/10/11 10:07:00 阅读更多 →

最新新闻

WinSxS 组件存储占用 C 盘?DISM 清理与修复指南

WinSxS 组件存储占用 C 盘?DISM 清理与修复指南

简介:面向Windows Server 2012 R2标准版运维人员的SXS源文件包,专门用于修复系统内置.NET Framework 3.5安装失败并需指定备用源路径的故障。资源按微软官方组件结构整理,聚合运行库、界面资源、配置项与数据库支持等多类依赖,可在…

2026/10/12 5:49:26 阅读更多 →
别再让 AI 瞎写 SAP 代码了!这套 Claude Code Skill 的验证机制才是关键

别再让 AI 瞎写 SAP 代码了!这套 Claude Code Skill 的验证机制才是关键

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 5:49:26 阅读更多 →
Grok Bot 视频制作全流程:从一句话需求到成片的高效方法

Grok Bot 视频制作全流程:从一句话需求到成片的高效方法

1. 从一句话到成片,这套流程到底在解决什么问题做视频这件事,门槛从来不在“拍”,而在“想清楚要什么、然后把它变成能发出去的成品”。我见过太多人卡在两个地方:一是脑子里有个模糊的想法,但不知道怎么落地成分镜和脚…

2026/10/12 5:49:26 阅读更多 →
FastAPI+HTML+SQLite3 扫码点餐系统开发全复盘

FastAPI+HTML+SQLite3 扫码点餐系统开发全复盘

前阵子帮朋友的小餐馆做了一个扫码点餐系统,技术栈就是标题里这套组合:FastAPI 提供后端接口,HTML 写两个前端页面——顾客扫码用的点餐页和店里用的后台管理系统,SQLite3 负责全部数据存储。后台管理系统挂在收银台电脑上&#x…

2026/10/12 5:49:26 阅读更多 →
BERT中文NER实战:从序列标注到微调避坑指南

BERT中文NER实战:从序列标注到微调避坑指南

简介:面向NLP初学者与算法工程师,这份实践项目以Python为编程语言,围绕预训练BERT模型实现中文命名实体识别(NER),可解决中文文本中人名、地名、组织名等实体自动抽取与标注的落地问题。压缩包共9个文件&am…

2026/10/12 5:49:26 阅读更多 →
递归函数与软件测试实战:从组合优化到协议生成的工程实践

递归函数与软件测试实战:从组合优化到协议生成的工程实践

我有个挺扎心的项目经验:一个软件测试工程师,被塞了个需求,要写代码自动生成离婚协议里的房产分割条款,核心算法用的是递归函数。乍一听像段子,但真做起来,我发现这项目正好把“递归”和“软件测试”这两个…

2026/10/12 5:48:25 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →