加速 Python for 循环(python加快循环速度)
off999 2024-10-23 12:50 37 浏览 0 评论
在接下来的内容中,我会给大家分享一些简便的方式,能够让 Python for 循环的速度实现 1.3 至 900 倍的提升。
Python 自身所具备的一个常用功能是 timeit 模块。在后续的几个部分中,我们会借助它来衡量循环当前的性能表现以及改进之后的性能状况。
对于每一种方法,我们都通过运行测试来设定基线,该测试涵盖了在 10 次测试运行中对被测函数执行 100K 次(循环),接着计算出每个循环的平均时间(以纳秒为单位,ns)。
几个简便方法
1、列表推导式
基线版本(低效方式)
计算数字的幂
未使用列表推导式
def test_01_v0(numbers):
output = []
for n in numbers:
output.append(n ** 2.5)
return output
改进版本
(使用列表推导式)
def test_01_v1(numbers):
output = [n ** 2.5 for n in numbers]
return output
测试结果如下:
测试结果汇总
基线:32.158 ns 每个循环
改进:16.040 ns 每个循环
% 改进:50.1 %
加速:2.00x
能够看到,运用列表推导式能够实现 2 倍速的提升。
2、在外部计算长度
倘若需要依据列表的长度进行迭代,那么就在 for 循环之外进行计算。
基线版本(低效方式)
(在 for 循环内部计算长度)
def test_02_v0(numbers):
output_list = []
for i in range(len(numbers)):
output_list.append(i * 2)
return output_list
改进版本
(在 for 循环外部计算长度)
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
通过将列表长度的计算移出 for 循环,实现了 1.6 倍的加速,或许这个方法鲜为人知。
测试结果汇总
基线:112.135 ns 每个循环
改进:68.304 ns 每个循环
% 改进:39.1 %
加速:1.64x
3、使用 Set
在通过 for 循环进行比较的情形下使用 set。
使用 for 循环进行嵌套查找
def test_03_v0(list_1, list_2):
基线版本(低效方式)
(使用 for 循环进行嵌套查找)
common_items = []
for item in list_1:
if item in list_2:
common_items.append(item)
return common_items
def test_03_v1(list_1, list_2):
改进版本
(使用 set 替代嵌套查找)
s_1 = set(list_1)
s_2 = set(list_2)
output_list = []
common_items = s_1.intersection(s_2)
return common_items
在使用嵌套 for 循环进行比较的情况下,使用 set 能够实现 498 倍的加速。
测试结果汇总
基线:9047.078 ns 每个循环
改进: 18.161 ns 每个循环
% 改进:99.8 %
加速:498.17x
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 = [i for n in numbers if n%2==0]
for n in even_numbers:
square = n * n
return square
return None # 未找到偶数平方
这个方法需要在设计 for 循环的内容时进行代码规划,具体能够提升的幅度可能因实际情况而有所不同:
测试结果汇总
基线:16.912 ns 每个循环
改进:8.697 ns 每个循环
% 改进:48.6 %
加速:1.94x
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):
基线版本(低效方式)
(多次调用 is_prime 函数)
count = 0
for i in range(2, n + 1):
if is_prime(i):
count += 1
return count
def test_05_v1(n):
改进版本
(将 is_prime 函数的逻辑内联)
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
这样也能够实现 1.3 倍的提升。
测试结果汇总
基线: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
结果如下
测试结果汇总
基线:139.146 ns 每个循环
改进:92.325 ns 每个循环
% 改进:33.6 %
加速:1.51x
7、使用 Generators
生成器支持延迟求值,也就是说,只有当向它请求下一个值时,内部的表达式才会被计算,动态处理数据有助于减少内存使用并提升性能。尤其是在处理大型数据集时
def test_08_v0(n):
基线版本(低效方式)
(低效地计算第 n 个斐波那契数,使用列表)
if n <= 1:
return n
f_list = [0, 1]
for i in range(2, n + 1):
f_list.append(f_list[i - 1] + f_list[i - 2])
return f_list[n]
def test_08_v1(n):
改进版本
(高效地计算第 n 个斐波那契数,使用生成器)
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b
能够看到提升非常显著:
测试结果汇总
基线:0.083 ns 每个循环
改进:0.004 ns 每个循环
% 改进:95.5 %
加速:22.06x
8、map()函数
使用 Python 内置的 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))
return output
def test_09_v1(numbers):
改进版本
(使用 Python 内置的 map()函数)
output = map(some_function_X, numbers)
return output
使用 Python 内置的 map()函数替代显式的 for 循环,实现了 970 倍的加速。
测试结果汇总
基线:4.402 ns 每个循环
改进:0.005 ns 每个循环
% 改进:99.9 %
加速:970.69x
这是为什么呢?
map()函数是用 C 语言编写的,并且经过了高度优化,因此它内部隐含的循环比常规的 Python for 循环高效得多。所以速度得到了提升,或者也可以说 Python 本身还是太慢,哈。
9、使用 Memoization
记忆优化算法的理念是缓存(或“记忆”)昂贵函数调用的结果,并在出现相同输入时返回它们。它能够减少冗余计算,加快程序速度。
首先是低效的版本。
低效代码示例
def fibonacci(n):
if n == 0:
return 0
elif n == 1:
return 1
return fibonacci(n - 1) + fibonacci(n-2)
def test_10_v0(list_of_numbers):
output = []
for i in numbers:
output.append(fibonacci(i))
return output
然后我们使用 Python 内置的 functools 的 lru_cache 函数。
高效代码示例
使用 Python 的 functools 的 lru_cache 函数
import functools
@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
结果如下:
测试结果汇总
基线:63.664 ns 每个循环
改进:1.104 ns 每个循环
% 改进:98.3 %
加速:57.69x
使用 Python 的内置 functools 的 lru_cache 函数并运用 Memoization 实现了 57 倍的加速。
lru_cache 函数是如何实现的?
“LRU”是“Least Recently Used”的缩写。lru_cache 是一个装饰器,能够应用于函数以启用 memoization。它会将最近函数调用的结果存储在缓存中,当再次出现相同的输入时,能够提供缓存的结果,从而节省计算时间。lru_cache 函数,在作为装饰器应用时,允许一个可选的 maxsize 参数,maxsize 参数决定了缓存的最大大小(即,它为多少个不同的输入值存储结果)。如果 maxsize 参数设置为 None,则禁用 LRU 特性,缓存可以不受限制地增长,这会消耗大量的内存。这是最简单的空间换时间的优化方法。
10、向量化
import numpy as np
def test_11_v0(n):
基线版本
(低效的求和方式)
output = 0
for i in range(0, n):
output = output + i
return output
def test_11_v1(n):
改进版本
(高效的求和方式)
output = np.sum(np.arange(n))
return output
向量化通常用于机器学习的数据处理库 numpy 和 pandas
测试结果汇总
基线:32.936 ns 每个循环
改进:1.171 ns 每个循环
% 改进:96.4 %
加速:28.13x
11、避免创建中间列表
使用 filterfalse 能够避免创建中间列表。它有助于减少内存使用。
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
使用 Python 内置的 itertools 的 filterfalse 函数实现相同功能的改进版本。
from itertools import filterfalse
def test_12_v1(numbers):
改进版本
(使用 filterfalse)
filtered_data = []
for i in numbers:
filtered_data.extend(list(
filterfalse(lambda x: x % 5!= 0,
range(1, i**2))))
return filtered_data
这个方法依据具体的用例,执行速度可能没有显著提升,但通过避免创建中间列表能够降低内存使用。我们在此获得了 131 倍的提升
测试结果汇总
基线:333167.790 ns 每个循环
改进:2541.850 ns 每个循环
% 改进:99.2 %
加速:131.07x
12、高效连接字符串
任何使用 + 操作符进行的字符串连接操作都会很慢,并且会消耗更多内存。使用 join 替代。
def test_13_v0(l_strings):
基线版本(低效方式)
(使用 += 操作符进行连接)
output = ""
for a_str in l_strings:
output += a_str
return output
def test_13_v1(numbers):
改进版本
(使用 join)
output_list = []
for a_str in l_strings:
output_list.append(a_str)
return "".join(output_list)
该测试需要一种简便的方法来生成一个较大的字符串列表,所以编写了一个简单的辅助函数来生成运行测试所需的字符串列表。
from faker import Faker
def generate_fake_names(count : int=10000):
辅助函数,用于生成
一个较大的名字列表
fake = Faker()
output_list = []
for _ in range(count):
output_list.append(fake.name())...
相关推荐
- 主题软件免费(主题软件免费推荐)
-
下载主题方法:一、打开手机找到APPStore应用软件,二、点击进去在下面找到有个搜索,点击它查找主题壁纸,三、弹出来很多主题壁纸,根据下载量和个人喜欢的应用主题选择下载相应的主题,四、下载成功后即...
- 一芯fc1178bc盘量产教程(一芯量产工具使用教程)
-
fc1178bc量产工具没有显示u盘拔下U盘,关闭量产工具,再插上U盘(先要确认卸载了安国的驱动,如果不能确认,运行LoadDriver.exe卸载),然后插上U盘,右键我的电脑---属性---硬件-...
- u盘内文件损坏怎么办(u盘内文件损坏怎么办解决)
-
以下是8种修复U盘文件损坏的方法:1.风险自担型:试图直接复制文件如果U盘的部分文件损坏,您可能可以使用此方法。请复制文件您能打开的所有文件,并尝试将它们粘贴到桌面或其他文件夹中。但是请...
- internet explorer怎么更新(22号天蝎座的运势)
-
1、打开IE浏览器。2、点击位于浏览器窗口的右上角的功能按钮。3、点击关于InternetExplorer。它位于下拉菜单的底部。4、勾选“自动安装新版本”复选框。它位于“关于Internet...
- snapseed(snapseed手机修图软件免费版)
-
Snapseed是一款非常流行的手机修图工具,下面是Snapseed工具最全教程:1.打开照片:打开Snapseed,点击左上角的“打开”按钮,选择需要修图的照片。2.自动增强:点击屏幕左下角的“...
- canon佳能打印机驱动下载(下载佳能打印机驱动程序)
-
打开开始菜单,选择运行。输入gpedit.msc,并确定。选择左边“windows设置”,右边鼠标左键双击“安全设置”。选择策略在选择安全选项再鼠标左键双击“设备:防止用户安装打印机驱动程序”。选择已...
- 爱思助手安卓版下载(爱思助手安卓版下载v1.21.03)
-
容易造成系统的崩溃在爱思助手中安装的软件都打不开或者发生闪退,很容易造成系统的崩溃需要重新刷机,所以一般不太推荐使用爱思助手。爱思助手上下载正版软件不需要AppleID,这是为了方便不会注册的用户,但...
-
- 微软拼音输入法app(微软拼音输入法App下载)
-
1、选择微软拼音输入法的图标,点击鼠标右键,出现菜单后选择设置选项。2、在高级里把美式键盘改为微软拼音输入法,然后点击右下角的属性按钮。3、点击逐键提示选项后,选择确定按钮,在后面出现对话框中点击应用即可。微软拼音输入法是一种基于语句的智能...
-
2025-12-31 04:51 off999
- win10怎么更新蓝牙驱动(win10 更新蓝牙驱动)
-
1.电脑桌面,右键【此电脑】,点击【属性】。2.然后点击【设备管理器】。3.然后展开【蓝牙】。4.然后鼠标右键【Bluetooth】,点击【更新驱动程序(P)】。5.选择一种方式更新驱动,更新完驱动就...
- 360免费升级正版win10(360 win10免费升级)
-
XP无法直接升级到Windows10. 能否升级还需要看硬件配置是否达标。如果达标可以通过以下方法来安装。 1、去系统网站下载win10镜像文件。 2、使用软碟通软件把镜像文件里面的gho....
- w7正版系统多少钱一年(正版win7旗舰版系统多少钱)
-
所有的正版windows系统都是需要付费购买的,包括笔记本电脑中预装好的正版系统,相应的费用也算入购机款中。你问的外行了。1、OEM系统是正版的,但是只能用于本品牌机上,也就是联想的WIN7系统(即O...
- excel2007破解版下载电脑版(excel 破解版)
-
现在excel2007可以说是免费软件,也可以说不是,因为现在在网上下载不了免费的2007年版excel软件,只能下载破译版的或用电信交钱下载。但有些电脑重装系统会带有2007年版的excel,这就是...
- comfast设置连接wifi(comfast路由器设置方法)
-
1,网关没有设置,2,DNS服务器没有设置,或者说设置成自动获取IP地址.具体不知道你是什么样的路由器,一般来说正常情况下网桥都是可以接收wifi信号的。所谓的网桥是使用有线网络连接到网桥设备上,然...
- windows电脑管家(windows电脑管家有用吗)
-
可以按照以下的方法步骤解决:1,在电脑左下角的搜索框内输入“电脑管家”,即可在电脑中匹配到该程序2,右键点击该程序图标,选择“打开文件位置”3,点击“打开文件位置”即可打开该程序在电脑中的地址窗口,右...
欢迎 你 发表评论:
- 一周热门
-
-
抖音上好看的小姐姐,Python给你都下载了
-
全网最简单易懂!495页Python漫画教程,高清PDF版免费下载
-
Python 3.14 的 UUIDv6/v7/v8 上新,别再用 uuid4 () 啦!
-
飞牛NAS部署TVGate Docker项目,实现内网一键转发、代理、jx
-
python入门到脱坑 输入与输出—str()函数
-
宝塔面板如何添加免费waf防火墙?(宝塔面板开启https)
-
Python三目运算基础与进阶_python三目运算符判断三个变量
-
(新版)Python 分布式爬虫与 JS 逆向进阶实战吾爱分享
-
失业程序员复习python笔记——条件与循环
-
系统u盘安装(win11系统u盘安装)
-
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python写入txt (66)
- python读取文件夹下所有文件 (59)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)
