加速 Python for 循环的12个操作

简介: 加速 Python for 循环的12个操作

在本文中,我将介绍几种简单而有效的方法,可以显著提高 Python 中 for 循环的执行速度,提升倍率从 1.3 倍到 900 倍不等。为了帮助你理解这些优化技巧,我们将使用 Python 内建的 timeit 模块来测量每种方法的性能。

通过对每种方法进行基线测试,包括在 10 次测试运行中执行被测函数 100,000 次循环,然后计算每个循环的平均时间(以纳秒为单位),我们可以清楚地看到改进后的效果。

1. 使用列表推导式

基线版本(低效的方式)

# 基础版本:未使用列表推导式
def test_01_v0(numbers):
    output = []
    for n in numbers:
        output.append(n ** 2.5)  # 计算每个数字的 2.5 次方
    return output

改进版本(使用列表推导式)

# 改进版本:使用列表推导式
def test_01_v1(numbers):
    output = [n ** 2.5 for n in numbers]  # 使用列表推导式计算
    return output

测试结果

import timeit
 
numbers = range(100000)
# Baseline performance
baseline_time = timeit.timeit("test_01_v0(numbers)", globals=globals(), number=10)
# Improved performance
improved_time = timeit.timeit("test_01_v1(numbers)", globals=globals(), number=10)
 
print(f"Baseline: {baseline_time / 10:.3f} ns per loop")
print(f"Improved: {improved_time / 10:.3f} ns per loop")
    return output

结果分析

  • 基线性能: 32.158 ns 每次循环
  • 改进性能: 16.040 ns 每次循环
  • 性能提升: 50.1%
  • 速度提升: 2.00x

使用列表推导式使得计算速度加快了 2 倍,提高了代码的简洁性和可读性。

2. 在外部计算长度

基线版本(低效的方式)

# 基础版本:在循环内部计算长度
def test_02_v0(numbers):
    output_list = []
    for i in range(len(numbers)):
        output_list.append(i * 2)  # 依赖于列表长度
    return output_list

改进版本(在外部计算长度)

# 改进版本:将长度计算移出循环
def test_02_v1(numbers):
    my_list_length = len(numbers)  # 提前计算长度
    output_list = []
    for i in range(my_list_length):
        output_list.append(i * 2)
    return output_list

测试结果

# Baseline performance
baseline_time = timeit.timeit("test_02_v0(numbers)", globals=globals(), number=10)
# Improved performance
improved_time = timeit.timeit("test_02_v1(numbers)", globals=globals(), number=10)
 
print(f"Baseline: {baseline_time / 10:.3f} ns per loop")
print(f"Improved: {improved_time / 10:.3f} ns per loop")

结果分析

  • 基线性能: 112.135 ns 每次循环
  • 改进性能: 68.304 ns 每次循环
  • 性能提升: 39.1%
  • 速度提升: 1.64x

将列表长度计算移出循环后,整体加速效果达到 1.64 倍,减少了多余的计算。

3. 使用 Set 进行集合比较

基线版本(低效的方式)

# 基础版本:嵌套循环查找公共元素
def test_03_v0(list_1, list_2):
    common_items = []
    for item in list_1:
        if item in list_2:  # 使用 for 循环进行查找
            common_items.append(item)
    return common_items

改进版本(使用 Set)

# 改进版本:使用集合替代嵌套循环
def test_03_v1(list_1, list_2):
    s_1 = set(list_1)  # 将列表转换为集合
    s_2 = set(list_2)  # 将列表转换为集合
    return list(s_1.intersection(s_2))  # 找出交集并返回

测试结果

list_1 = range(10000)
list_2 = range(5000, 15000)
 
# Baseline performance
baseline_time = timeit.timeit("test_03_v0(list_1, list_2)", globals=globals(), number=10)
# Improved performance
improved_time = timeit.timeit("test_03_v1(list_1, list_2)", globals=globals(), number=10)
 
print(f"Baseline: {baseline_time / 10:.3f} ns per loop")
print(f"Improved: {improved_time / 10:.3f} ns per loop")

结果分析

  • 基线性能: 9047.078 ns 每次循环
  • 改进性能: 18.161 ns 每次循环
  • 性能提升: 99.8%
  • 速度提升: 498.17x

在使用集合后,查找公共元素的速度提升显著,达到了近 500 倍的提升。

4. 跳过不相关的迭代

基线版本(低效的方式)

# 基础版本:冗余计算
def function_do_something(numbers):
    for n in numbers:
        square = n * n  # 计算平方
        if square % 2 == 0:  # 检查是否为偶数
            return square  # 返回第一个偶数平方
    return None  # 未找到偶数平方

改进版本(避免冗余计算)

# 改进版本:先过滤偶数
def function_do_something_v1(numbers):
    even_numbers = [n for n in numbers if n % 2 == 0]  # 先筛选偶数
    for n in even_numbers:
        square = n * n
        return square  # 返回第一个偶数平方
    return None  # 未找到偶数平方

测试结果

# Baseline performance
baseline_time = timeit.timeit("function_do_something(range(100000))", globals=globals(), number=10)
# Improved performance
improved_time = timeit.timeit("function_do_something_v1(range(100000))", globals=globals(), number=10)
 
print(f"Baseline: {baseline_time / 10:.3f} ns per loop")
print(f"Improved: {improved_time / 10:.3f} ns per loop"

结果分析

  • 基线性能: 16.912 ns 每次循环
  • 改进性能: 8.697 ns 每次循环
  • 性能提升: 48.6%
  • 速度提升: 1.94x

通过跳过不相关的迭代,性能提高了近 2 倍,使得计算更加高效。

5. 代码合并以减少函数调用

基线版本(低效的方式)

# 基础版本:多次调用 is_prime 函数
def is_prime(n):
    if n <= 1:
        return False
    for i in range(2, int(n**0.5) + 1):
        if n % i == 0:
            return False
    return True
 
def test_05_v0(n):
    count = 0
    for i in range(2, n + 1):  # 从 2 到 n 之间的数
        if is_prime(i):  # 调用 is_prime 函数
            count += 1
    return count

改进版本(内联逻辑)

# 改进版本:将 is_prime 的逻辑内联到循环中
def test_05_v1(n):
    count = 0
    for i in range(2, n + 1):
        if i <= 1:
            continue
        for j in range(2, int(i**0.5) + 1):
            if i % j == 0:
                break
        else:
            count += 1
    return count

测试结果

# Baseline performance
baseline_time = timeit.timeit("test_05_v0(1000)", globals=globals(), number=10)
# Improved performance
improved_time = timeit.timeit("test_05_v1(1000)", globals=globals(), number=10)
 
print(f"Baseline: {baseline_time / 10:.3f} ns per loop")
print(f"Improved: {improved_time / 10:.3f} ns per loop")

结果分析

  • 基线性能: 1271.188 ns 每次循环
  • 改进性能: 939.603 ns 每次循环
  • 性能提升: 26.1%
  • 速度提升: 1.35x

将函数逻辑内联到循环中减少了函数调用的开销,从而提高了性能。

6. 避免重复计算

基线版本(低效的方式)

# 基础版本:在嵌套循环中重复计算
def test_07_v0(n):
    result = 0
    for i in range(n):
        for j in range(n):
            result += i * j  # 重复计算
    return result

改进版本(利用预计算)

# 改进版本:使用预计算值
def test_07_v1(n):
    pv = [[i * j for j in range(n)] for i in range(n)]  # 预计算
    result = 0
    for i in range(n):
        result += sum(pv[i][:i + 1])  # 使用预计算的值
    return result

测试结果

# Baseline performance
baseline_time = timeit.timeit("test_07_v0(100)", globals=globals(), number=10)
# Improved performance
improved_time = timeit.timeit("test_07_v1(100)", globals=globals(), number=10)
 
print(f"Baseline: {baseline_time / 10:.3f} ns per loop")
print(f"Improved: {improved_time / 10:.3f} ns per loop")

结果分析

  • 基线性能: 139.146 ns 每次循环
  • 改进性能: 92.325 ns 每次循环
  • 性能提升: 33.6%
  • 速度提升: 1.51x

通过预计算避免了重复计算,性能提高明显。

7. 使用生成器

基线版本(低效的方式)

# 基础版本:使用列表计算 Fibonacci 数列
def test_08_v0(n):
    f_list = [0, 1]
    for i in range(2, n + 1):
        f_list.append(f_list[i - 1] + f_list[i - 2])  # 计算 Fibonacci
    return f_list[n]

改进版本(使用生成器)

# 改进版本:使用生成器动态计算 Fibonacci
def test_08_v1(n):
    a, b = 0, 1
    for _ in range(n):
        yield a  # 生成当前 Fibonacci 值
        a, b = b, a + b

测试结果

# Baseline performance
baseline_time = timeit.timeit("test_08_v0(100)", globals=globals(), number=10)
# Improved performance (using generator)
improved_time = timeit.timeit("list(test_08_v1(100))", globals=globals(), number=10)
 
print(f"Baseline: {baseline_time / 10:.3f} ns per loop")
print(f"Improved: {improved_time / 10:.3f} ns per loop")

结果分析

  • 基线性能: 0.083 ns 每次循环
  • 改进性能: 0.004 ns 每次循环
  • 性能提升: 95.5%
  • 速度提升: 22.06x

使用生成器不仅可以节省内存,还能显著提高性能。

8. 使用 map() 函数

基线版本(低效的方式)

# 基础版本:使用 for 循环处理列表
def some_function_X(x):
    return x ** 2  # 执行某些操作
 
def test_09_v0(numbers):
    output = []
    for i in numbers:
        output.append(some_function_X(i))  # 使用显式 for 循环
    return output

改进版本(使用 map())

# 改进版本:使用内置的 map() 函数
def test_09_v1(numbers):
    return list(map(some_function_X, numbers))  # 使用 map

测试结果

# Baseline performance
baseline_time = timeit.timeit("test_09_v0(numbers)", globals=globals(), number=10)
# Improved performance
improved_time = timeit.timeit("test_09_v1(numbers)", globals=globals(), number=10)
 
print(f"Baseline: {baseline_time / 10:.3f} ns per loop")
print(f"Improved: {improved_time / 10:.3f} ns per loop")

结果分析

  • 基线性能: 4.402 ns 每次循环
  • 改进性能: 0.005 ns 每次循环
  • 性能提升: 99.9%
  • 速度提升: 970.69x

使用 map() 显著提高了处理速度,因为它是用 C 语言实现的,性能更优。

9. 使用记忆化(Memoization)

基线版本(低效的方式)

# 基础版本:递归计算 Fibonacci 数列
def fibonacci(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    return fibonacci(n - 1) + fibonacci(n - 2)
 
def test_10_v0(numbers):
    output = []
    for i in numbers:
        output.append(fibonacci(i))  # 计算 Fibonacci
    return output

改进版本(使用 lru_cache)

import functools
 
# 使用 lru_cache 进行记忆化
@functools.lru_cache()
def fibonacci_v2(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    return fibonacci_v2(n - 1) + fibonacci_v2(n - 2)
 
def test_10_v1(numbers):
    output = []
    for i in numbers:
        output.append(fibonacci_v2(i))  # 利用记忆化计算
    return output

测试结果

# Baseline performance
baseline_time = timeit.timeit("test_10_v0(range(30))", globals=globals(), number=10)
# Improved performance
improved_time = timeit.timeit("test_10_v1(range(30))", globals=globals(), number=10)
 
print(f"Baseline: {baseline_time / 10:.3f} ns per loop")
print(f"Improved: {improved_time / 10:.3f} ns per loop")

结果分析

  • 基线性能: 63.664 ns 每次循环
  • 改进性能: 1.104 ns 每次循环
  • 性能提升: 98.3%
  • 速度提升: 57.69x

通过使用 lru_cache 实现记忆化,成功减少了函数调用的重复计算,大幅提升性能。

10. 向量化(利用 NumPy)

基线版本(低效的方式)

import numpy as np
 
# 基础版本:使用 for 循环计算和
def test_11_v0(n):
    output = 0
    for i in range(0, n):
        output += i  # 累加数字
    return output

改进版本(使用 NumPy 向量化)

# 改进版本:使用 NumPy 向量化计算
def test_11_v1(n):
    return np.sum(np.arange(n))  # 利用 NumPy 计算和

测试结果

# Baseline performance
baseline_time = timeit.timeit("test_11_v0(10000)", globals=globals(), number=10)
# Improved performance
improved_time = timeit.timeit("test_11_v1(10000)", globals=globals(), number=10)
 
print(f"Baseline: {baseline_time / 10:.3f} ns per loop")
print(f"Improved: {improved_time / 10:.3f} ns per loop")

结果分析

  • 基线性能: 32.936 ns 每次循环
  • 改进性能: 1.171 ns 每次循环
  • 性能提升: 96.4%
  • 速度提升: 28.13x

向量化利用 NumPy 的快速运算,显著提高了性能。

11. 避免创建中间列表

基线版本(低效的方式)

# 基础版本:创建中间列表
def test_12_v0(numbers):
    filtered_data = []
    for i in numbers:
        filtered_data.extend(list(filter(lambda x: x % 5 == 0, range(1, i**2))))  # 创建中间列表
    return filtered_data

改进版本(使用 filterfalse)

from itertools import filterfalse
 
# 改进版本:使用 filterfalse 避免中间列表
def test_12_v1(numbers):
    filtered_data = []
    for i in numbers:
        filtered_data.extend(list(filterfalse(lambda x: x % 5 != 0, range(1, i**2))))  # 直接过滤
    return filtered_data

测试结果

# Baseline performance
baseline_time = timeit.timeit("test_12_v0(range(100))", globals=globals(), number=10)
# Improved performance
improved_time = timeit.timeit("test_12_v1(range(100))", globals=globals(), number=10)
 
print(f"Baseline: {baseline_time / 10:.3f} ns per loop")
print(f"Improved: {improved_time / 10:.3f} ns per loop")

结果分析

  • 基线性能: 333167.790 ns 每次循环
  • 改进性能: 2541.850 ns 每次循环
  • 性能提升: 99.2%
  • 速度提升: 131.07x

通过避免创建中间列表,显著降低了内存使用,同时提高了性能。

12. 高效连接字符串

基线版本(低效的方式)

# 基础版本:使用 + 操作符连接字符串
def test_13_v0(l_strings):
    output = ""
    for a_str in l_strings:
        output += a_str  # 字符串连接
    return output

改进版本(使用 join)

# 改进版本:使用 join 函数连接字符串
def test_13_v1(l_strings):
    return "".join(l_strings)  # 使用 join 连接

测试结果

from faker import Faker
 
def generate_fake_names(count: int = 10000):
    fake = Faker()
    output_list = []
    for _ in range(count):
        output_list.append(fake.name())
    return output_list
 
l_strings = generate_fake_names(count=50000)
 
# Baseline performance
baseline_time = timeit.timeit("test_13_v0(l_strings)", globals=globals(), number=10)
# Improved performance
improved_time = timeit.timeit("test_13_v1(l_strings)", globals=globals(), number=10)
 
print(f"Baseline: {baseline_time / 10:.3f} ns per loop")
print(f"Improved: {improved_time / 10:.3f} ns per loop")

结果分析

  • 基线性能: 32.423 ns 每次循环
  • 改进性能: 21.051 ns 每次循环
  • 性能提升: 35.1%
  • 速度提升: 1.54x

使用 join() 函数而不是 + 运算符连接字符串的性能得到了提升,这是因为 join 的时间复杂度为 O(n),而 + 的时间复杂度为 O(n²)。

总结

本文介绍了一系列有效的 Python 优化技巧,这些技巧可以提升 for 循环的性能,从 1.3 倍到 970 倍。这些方法包括:

  • 使用列表推导式
  • 在外部计算长度
  • 使用集合取代嵌套循环
  • 跳过不相关的迭代
  • 合并代码以减少函数调用
  • 避免重复计算
  • 使用生成器
  • 使用 map() 函数
  • 使用记忆化(Memoization)
  • 向量化操作
  • 避免创建中间列表
  • 高效连接字符串


掌握这些技巧将帮助你编写更高效、更优雅的 Python 代码,提升你的编程水平和项目性能。希望这篇文章能为你提供有价值的参考与启发!

相关文章
|
JavaScript
JS自动生成速记符、拼音简写/拼音的声母(例如:“你挚爱的强哥”转换为“NZADQG”)。提取首字母,返回大写形式;提取拼音, 返回首字母大写形式(全拼)。
JS自动生成速记符、拼音简写/拼音的声母(例如:“你挚爱的强哥”转换为“NZADQG”)。提取首字母,返回大写形式;提取拼音, 返回首字母大写形式(全拼)。
28625 0
|
开发工具 git 开发者
2024最简七步完成 将本地项目提交到github仓库方法
该文章提供了一个简洁的七步教程,指导用户如何将本地项目提交到GitHub仓库。
2024最简七步完成 将本地项目提交到github仓库方法
|
存储 传感器 自动驾驶
几种常见的点云格式数据解析与在线预览
3D模型在线转换网站支持pcd、pts、xyz、las、laz、asc、ply等点云格式文件在线预览,同时支持将点云格式在线转换为ply、xyz等模型格式。
9336 1
|
5月前
|
机器学习/深度学习 数据采集 人工智能
专用蚊子苍蝇检测数据集分享(适用于目标检测任务含背景样本)
本数据集专为蚊子/苍蝇目标检测设计,含1500+张高清图像(600+蚊子、600+苍蝇、200+背景样本),全部采用YOLO标准格式标注,适配YOLOv8/v5/v7。含训练/验证集划分,支持小目标检测与强泛化,适用于智能灭蚊、疾控监测及AI教学。百度网盘免费下载(提取码:dcar)。
472 0
|
并行计算 算法 编译器
使用 prange 实现 for 循环的并行
使用 prange 实现 for 循环的并行
774 1
使用 prange 实现 for 循环的并行
|
存储 数据可视化 知识图谱
高效知识管理的五大方法,助力写作更上一层楼
在信息爆炸的时代,高效的知识管理是提升写作效率与质量的关键。本文探讨如何通过系统化的知识获取、分类存储、动态更新和高效检索,构建个人知识体系,并介绍智能化工具如看板的应用,帮助写作者整合信息、激发灵感、优化流程,实现从输入到输出的闭环,持续提升创作能力。
516 14
高效知识管理的五大方法,助力写作更上一层楼
|
Java 调度 Python
多线程优化For循环:实战指南
本文介绍如何使用多线程优化For循环,提高程序处理大量数据或耗时操作的效率。通过并行任务处理,充分利用多核处理器性能,显著缩短执行时间。文中详细解释了多线程基础概念,如线程、进程、线程池等,并提供了Python代码示例,包括单线程、多线程和多进程实现方式。最后,还总结了使用多线程或多进程时需要注意的事项,如线程数量、任务拆分、共享资源访问及异常处理等。
764 7
|
Python
Python实用记录(十一):出现科学计数法如何在代码中恢复
本文介绍了如何在Python中处理科学计数法,包括如何将科学计数法转换为普通数字,以及如何设置NumPy的print选项来避免科学计数法的显示。
529 1
|
监控 数据可视化 安全
软件生命周期是什么?包括哪些阶段?各阶段的目标和任务是什么?
在数字化时代,软件如同空气般无处不在,其生命周期涵盖从需求分析到退役的多个阶段,如同生物的成长过程。本文详细介绍了软件生命周期各阶段的目标与任务,并探讨了瀑布模型、迭代模型和敏捷模型等常见生命周期模型。未来,随着技术和业务的不断演变,软件生命周期管理将面临更多挑战与机遇,需不断学习先进方法和技术,以满足用户需求。
8367 0
|
Cloud Native 关系型数据库 分布式数据库
开发者如何使用云原生数据库PolarDB
【10月更文挑战第5天】开发者如何使用云原生数据库PolarDB
692 2