page contents

列举Python中常见的性能瓶颈,并提供优化方法!

写 Python 的人多少都经历过:代码写完能跑,但一到数据量大就慢得受不了。很多时候不是机器不给力,而是我们自己写法“埋雷”。Python 不是没救,只要知道常见的性能瓶颈,再对症下药,速度能提不少。下面我挑一些最常见的坑,用比较通俗的例子说说,字数控制在一千五左右。

attachments-2025-10-2aScwixa68eefab904dfe.png写 Python 的人多少都经历过:代码写完能跑,但一到数据量大就慢得受不了。很多时候不是机器不给力,而是我们自己写法“埋雷”。Python 不是没救,只要知道常见的性能瓶颈,再对症下药,速度能提不少。下面我挑一些最常见的坑,用比较通俗的例子说说,字数控制在一千五左右。

先量化再改,不要盲调

优化之前先测。常用的工具一个是 cProfile,能看哪段代码最耗时;另一个是 timeit,方便对比两种写法的快慢。

import cProfile

def slow():
    s = ""
    for i in range(10000):
        s += str(i)
    return s

cProfile.run("slow()")

这样能直接告诉你哪一步拖了后腿,比凭感觉乱改靠谱多了。

算法复杂度,决定上限

典型问题:明明 O(n) 能做的事,结果写成 O(n²)。比如去重,双循环慢到怀疑人生,用 set 或 dict.fromkeys 就好。

def dedup_fast(xs):
    return list(dict.fromkeys(xs))

这个小技巧不仅快,还能保持原有顺序。

字符串拼接,循环里千万别 “+”

在循环里 s += ... 是很多人的习惯,但性能差得离谱,因为每次拼接都会生成新对象。正确方式是一次性 "".join(parts)

parts = [str(i) for i in range(10000)]
s = "".join(parts)

局部变量绑定,少查一次就是赚

Python 查找变量是逐层字典找的,大循环里如果频繁访问全局函数,能慢不少。提前把函数绑定到局部变量,效果立竿见影。

def tight_loop():
    rng = range
    s = 0
    for i in rng(1000000):
        s += i
    return s

Python 的对象模型本来就比较重,如果创建太多小对象,垃圾回收会花掉不少时间。解决思路:能用内置类型就别造类;如果一定要类,给它加上 __slots__

class Point:
    __slots__ = ("x""y")
    def __init__(self, x, y):
        self.x, self.y = x, y

这样能省内存,访问速度也更快。

异常别当流程控制

用异常当分支判断会很慢,因为抛异常和捕获的代价都高。能用 if key in dict 或 dict.get 就别用 try/except。

val = mapping.get(key, default)

数值运算,别用纯 Python for

要是大规模数值计算,千万别用 for 循环硬算,直接上 NumPy。

import numpy as np
x = np.arange(1e6)
y = np.arange(1e6)
z = np.dot(x, y)

这类运算在 C 层跑,比 Python 循环快几个数量级。

I/O 阻塞:CPU 在等你

网络请求、磁盘读写这些任务 CPU 其实没干活,线程都在等结果。这种场景可以用线程池或异步。

from concurrent.futures import ThreadPoolExecutor
import requests

def fetch(url):
    return requests.get(url).text

urls = ["https://example.com"] * 20
with ThreadPoolExecutor(max_workers=5as ex:
    results = list(ex.map(fetch, urls))

异步的 aiohttp 也不错,但要注意别混用阻塞库。

CPU 密集别用线程,GIL 会卡死

Python 有全局解释器锁(GIL),导致 CPU 密集任务开多少线程都没用。要想利用多核,就得用多进程。

from concurrent.futures import ProcessPoolExecutor

def calc(n):
    s = 0
    for i in range(n):
        s += i*i
    return s

with ProcessPoolExecutor() as ex:
    results = ex.map(calc, [10_000_00]*4)

正则表达式:贪婪模式容易翻车

复杂正则里一个 .* 就可能让程序卡死。最好加边界或改成更确定的模式。

import re
pat = re.compile(r"\d{4}-\d{2}-\d{2}")  # 明确日期格式

比模糊匹配快又稳。

日志打印,别在热循环里乱来

日志输出涉及字符串拼接和 I/O,频繁调用 debug 会让程序变慢。要么关掉日志,要么延迟格式化。

import logging
log = logging.getLogger(__name__)
log.debug("user=%s items=%d", uid, len(items))

缓存能救命

有些函数其实是重复计算的,像递归求斐波那契,加个缓存装饰器就能快很多。

from functools import lru_cache

@lru_cache
def fib(n):
    if n < 2return n
    return fib(n-1) + fib(n-2)

内存拷贝,能省就省

切片其实会复制数据,处理大对象时成本很高。可以用生成器或者 memoryview

data = b"abcdefghij"
mv = memoryview(data)
print(mv[2:5])  # 不复制

pandas:避免行级循环

数据分析时最容易踩的坑就是 iterrows。几百万行数据一行行处理,速度惨不忍睹。换成向量化操作就轻松很多。

import pandas as pd
df = pd.DataFrame({"x": range(1000000)})
df["y"] = df["x"] * 2

Python 优化其实没有玄学,套路就几个:先找瓶颈,再换更合适的数据结构或库;CPU 密集就靠 NumPy 或多进程,I/O 密集就靠线程或异步;少造对象,少用异常,多利用缓存和生成器。别一上来怪语言慢,大多数时候是写法问题。

更多相关技术内容咨询欢迎前往并持续关注好学星城论坛了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

  • 发表于 2025-10-15 09:37
  • 阅读 ( 27 )
  • 分类:Python开发

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

1479 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 2228 文章
  3. Pack 1479 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章