楼主: CDA网校
103 0

如何加速缓慢的Python代码(即使你是初学者) [推广有奖]

管理员

已卖:189份资源

泰斗

7%

还不是VIP/贵宾

-

威望
3
论坛币
137248 个
通用积分
14328.8628
学术水平
283 点
热心指数
291 点
信用等级
258 点
经验
234534 点
帖子
7356
精华
19
在线时间
4466 小时
注册时间
2019-9-13
最后登录
2026-5-7

初级热心勋章

楼主
CDA网校 学生认证  发表于 2026-4-1 14:30:13 |AI写论文

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币

引言

Python是最适合初学者的编程语言之一。但如果你使用它有一段时间,可能会遇到运行几分钟才能完成的循环、占用全部内存的数据处理任务等等。

Airia 企业级AI

你无需成为性能优化专家,就能实现显著的改进。大多数缓慢的Python代码,都是由几个常见问题导致的——一旦你知道要关注哪些点,这些问题就能轻松解决。

在本文中,你将学习5种实用技巧来加速缓慢的Python代码,并通过“优化前vs优化后”的示例,直观看到效果差异。

你可以在GitHub上找到本文的相关代码。

前置条件

开始之前,请确保你已具备以下条件:

  • 安装Python 3.10及以上版本

  • 熟悉函数、循环和列表的使用

  • 对标准库中的time模块有一定了解

部分示例还需要你安装以下库:

pip install pandas numpy

1. 优化前先进行测量

在修改任何一行代码之前,你需要明确代码的缓慢之处到底在哪里。优化错误的代码部分不仅浪费时间,甚至可能让情况变得更糟。

Python标准库提供了一种简单的方法来计时任何代码块:time模块。如需更详细的性能分析,cProfile可以精确显示哪些函数耗时最长。

假设你有一个处理销售记录列表的脚本,以下是找到缓慢部分的方法:

import time

def load_records():
    # 模拟加载100,000条记录
    return list(range(100_000))

def filter_records(records):
    return [r for r in records if r % 2 == 0]

def generate_report(records):
    return sum(records)

# 计时每一步操作
start = time.perf_counter()
records = load_records()
print(f"加载数据 : {time.perf_counter() - start:.4f}秒")

start = time.perf_counter()
filtered = filter_records(records)
print(f"筛选数据 : {time.perf_counter() - start:.4f}秒")

start = time.perf_counter()
report = generate_report(filtered)
print(f"生成报告 : {time.perf_counter() - start:.4f}秒")

输出结果:

加载数据 : 0.0034秒
筛选数据 : 0.0060秒
生成报告 : 0.0012秒

现在你知道该重点优化哪里了:filter_records()是最耗时的步骤,其次是load_records()。因此,优化这两个部分才能获得最大收益。如果不进行测量,你可能会浪费时间去优化本就很快的generate_report()。

对于短时间测量,time.perf_counter()比time.time()更精确。只要你需要计时代码性能,就使用它。

经验法则:永远不要猜测性能瓶颈在哪里。先测量,再优化。

2. 使用内置函数和标准库工具

Python的内置函数——sum()、map()、filter()、sorted()、min()、max()——底层是用C语言实现的。它们比用纯Python循环编写等效逻辑要快得多。

我们来对比一下“手动求和列表”和“使用内置sum()函数求和”的效率:

import time

numbers = list(range(1_000_000))

# 手动循环求和
start = time.perf_counter()
total = 0
for n in numbers:
    total += n
print(f"手动循环 : {time.perf_counter() - start:.4f}秒  →  {total}")

# 内置sum()函数求和
start = time.perf_counter()
total = sum(numbers)
print(f"内置函数    : {time.perf_counter() - start:.4f}秒  →  {total}")

输出结果:

手动循环 : 0.1177秒  →  499999500000
内置函数    : 0.0103秒  →  499999500000

正如你所见,使用内置函数的速度几乎是手动循环的6倍。

同样的原理也适用于排序。如果你需要根据键对字典列表进行排序,Python的sorted()函数(搭配key参数)比手动排序更快、更简洁。以下是另一个示例:

orders = [
    {"id""ORD-003""amount"250.0},
    {"id""ORD-001""amount"89.99},
    {"id""ORD-002""amount"430.0},
]

# 缓慢:手动比较逻辑
def manual_sort(orders):
    for i in range(len(orders)):
        for j in range(i + 1, len(orders)):
            if orders[i]["amount"] > orders[j]["amount"]:
                orders[i], orders[j] = orders[j], orders[i]
    return orders

# 快速:内置sorted()函数
sorted_orders = sorted(orders, key=lambda o: o["amount"])
print(sorted_orders)

输出结果:

[{'id': 'ORD-001', 'amount': 89.99}, {'id': 'ORD-003', 'amount': 250.0}, {'id': 'ORD-002', 'amount': 430.0}]

作为练习,你可以尝试为上述两种方法计时。

经验法则:当你需要编写循环来完成常见操作(求和、排序、找最大值等)时,先检查Python是否已有对应的内置函数。几乎总能找到,而且速度几乎总是更快。

3. 避免在循环内重复执行耗时操作

最常见的性能错误之一,是在循环内执行本可以在循环外一次性完成的耗时操作。每次循环都会付出相应代价,即使操作结果从未改变。

以下示例:根据批准列表验证一系列产品代码。

import time

approved = ["SKU-001""SKU-002""SKU-003""SKU-004""SKU-005"] * 1000
incoming = [f"SKU-{str(i).zfill(3)}" for i in range(5000)]

# 缓慢:每次循环都执行列表长度计算和成员检查
start = time.perf_counter()
valid = []
for code in incoming:
    if code in approved:        # 列表查找时间复杂度为O(n)——缓慢
        valid.append(code)
print(f"列表检查 : {time.perf_counter() - start:.4f}秒  →  {len(valid)}个有效代码")

# 快速:循环前将批准列表转换为集合(仅执行一次)
start = time.perf_counter()
approved_set = set(approved)    # 集合查找时间复杂度为O(1)——快速
valid = []
for code in incoming:
    if code in approved_set:
        valid.append(code)
print(f"集合检查  : {time.perf_counter() - start:.4f}秒  →  {len(valid)}个有效代码")

输出结果:

列表检查 : 0.3769秒  →  5个有效代码
集合检查  : 0.0014秒  →  5个有效代码

第二种方法快得多,而改进仅仅是将一次类型转换移到了循环外面。

同样的模式适用于所有“循环中结果不变”的耗时操作,比如读取配置文件、编译正则表达式、打开数据库连接等。这些操作应在循环前执行一次,而不是每次循环都执行。

import re

# 缓慢:每次调用都重新编译正则表达式
def extract_slow(text):
    return re.findall(r'\d+', text)

# 快速:编译一次,重复使用
DIGIT_PATTERN = re.compile(r'\d+')

def extract_fast(text):
    return DIGIT_PATTERN.findall(text)

经验法则:如果循环内某一行代码的结果在每次循环中都相同,就把它移到循环外面。

4. 选择合适的数据结构

Python提供了多种内置数据结构——列表(list)、集合(set)、字典(dict)、元组(tuple)——为任务选择错误的数据结构,会让你的代码比必要情况慢得多。

最关键的区别在于,使用in运算符进行成员检查时,列表和集合的效率差异:

  • 检查一个元素是否在列表中,耗时会随着列表长度增加而变长,因为需要逐个扫描列表

  • 集合使用哈希表实现,无论集合大小如何,都能在固定时间内完成成员检查

以下示例:从大型数据集中找出已经下过单的客户ID。

import time
import random

all_customers = [f"CUST-{i}" for i in range(100_000)]
ordered = [f"CUST-{i}" for i in random.sample(range(100_000), 10_000)]

# 缓慢:ordered是列表
start = time.perf_counter()
repeat_customers = [c for c in all_customers if c in ordered]
print(f"列表查找 : {time.perf_counter() - start:.4f}秒  →  找到{len(repeat_customers)}个重复客户")

# 快速:ordered转换为集合
ordered_set = set(ordered)
start = time.perf_counter()
repeat_customers = [c for c in all_customers if c in ordered_set]
print(f"集合查找  : {time.perf_counter() - start:.4f}秒  →  找到{len(repeat_customers)}个重复客户")

输出结果:

列表查找 : 16.7478秒  →  找到10000个重复客户
集合查找  : 0.0095秒  →  找到10000个重复客户

同样的逻辑也适用于字典(需要快速键查找时),以及collections模块的deque(需要频繁从序列两端添加或删除元素时——这是列表的薄弱环节)。

以下是快速参考指南,帮助你选择合适的数据结构:

需求 推荐使用的数据结构
有序序列、索引访问 list(列表)
快速成员检查 set(集合)
键值对查找 dict(字典)
统计元素出现次数 collections.Counter
队列或双端队列操作 collections.deque

经验法则:如果在循环中使用“x in 某对象”进行成员检查,且该对象包含超过几百个元素,那么它应该是一个集合(set)。

5. 对数值数据使用向量化操作

如果你的代码需要处理数值——比如跨数据行计算、统计操作、数据转换——编写Python循环几乎总是最慢的方式。NumPy和pandas等库正是为这类场景设计的:它们能在优化后的C代码中一次性对整个数组执行操作,完全无需Python循环。

这就是所谓的“向量化”。你无需告诉Python逐个处理每个元素,只需将整个数组交给函数,函数会在内部以C语言的速度完成所有操作。

import time
import numpy as np
import pandas as pd

prices = [round(10 + i * 0.052for i in range(500_000)]
discount_rate = 0.15

# 缓慢:Python循环
start = time.perf_counter()
discounted = []
for price in prices:
    discounted.append(round(price * (1 - discount_rate), 2))
print(f"Python循环 : {time.perf_counter() - start:.4f}秒")

# 快速:NumPy向量化
prices_array = np.array(prices)
start = time.perf_counter()
discounted = np.round(prices_array * (1 - discount_rate), 2)
print(f"NumPy        : {time.perf_counter() - start:.4f}秒")

# 快速:pandas向量化
prices_series = pd.Series(prices)
start = time.perf_counter()
discounted = (prices_series * (1 - discount_rate)).round(2)
print(f"pandas       : {time.perf_counter() - start:.4f}秒")

输出结果:

Python循环 : 1.0025秒
NumPy        : 0.0122秒
pandas       : 0.0032秒

对于这项操作,NumPy的速度几乎是Python循环的100倍。代码也更简洁:没有循环,没有append(),只有一行表达式。

如果你已经在使用pandas的Datafr ame,同样的原理也适用于列操作。永远优先选择列级操作,而不是用iterrows()逐行循环:

df = pd.Datafr ame({"price": prices})

# 缓慢:用iterrows()逐行处理
start = time.perf_counter()
for idx, row in df.iterrows():
    df.at[idx, "discounted"] = round(row["price"] * 0.852)
print(f"iterrows方法 : {time.perf_counter() - start:.4f}秒")

# 快速:向量化列操作
start = time.perf_counter()
df["discounted"] = (df["price"] * 0.85).round(2)
print(f"向量化操作 : {time.perf_counter() - start:.4f}秒")

输出结果:

iterrows方法 : 34.5615秒
向量化操作 : 0.0051秒

iterrows()是pandas中最常见的性能陷阱之一。如果你的代码中使用了它,且处理的数据超过几千行,将其替换为列操作几乎总是值得的。

经验法则:如果你的代码在循环处理数值或Datafr ame行,不妨问问自己——NumPy或pandas是否能通过向量化操作完成同样的事情。

结论

缓慢的Python代码通常是“模式问题”。优化前先测量、依赖内置函数、避免循环内重复工作、选择合适的数据结构、对数值数据使用向量化操作——这5个技巧,能解决初学者遇到的绝大多数性能问题。

每次优化都从第一步开始:测量。找到真正的性能瓶颈,修复它,然后再次测量。你会惊讶地发现,在需要更高级的优化方法之前,代码还有很大的性能提升空间。

本文介绍的5种技巧,覆盖了Python代码缓慢的最常见原因。但有时你可能需要更进一步:

  • 多进程(Multiprocessing)——如果你的任务是CPU密集型,且使用的是多核机器,Python的multiprocessing模块可以将任务分配到多个核心上

  • 异步I/O(Async I/O)——如果你的代码大部分时间都在等待网络请求或文件读取,asyncio可以同时处理多个任务

  • Dask或Polars——对于无法放入内存的大型数据集,这些库的扩展性远超pandas

当你应用完基础技巧后,若仍需要更高的性能,再去探索这些进阶方法也不迟。编程愉快!

推荐学习书籍 《CDA一级教材》适合CDA一级考生备考,也适合业务及数据分析岗位的从业者提升自我。完整电子版已上线CDA网校,累计已有10万+在读~ !

免费加入阅读:https://edu.cda.cn/goods/show/3151?targetId=5147&preview=0

二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:python 如何加速 初学者 collections Collection

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
扫码
拉您进交流群
GMT+8, 2026-5-8 01:18