天天看点

for循环优化_Python中的循环比较和性能

Python是当今最受欢迎的编程语言之一。这是一种具有优雅且易读语法的解释性高级语言。但是,Python通常比Java,C#尤其是C,C ++或Fortran慢得多。有时性能问题和瓶颈可能会严重影响应用程序的可用性。

幸运的是,在大多数情况下,有一些解决方案可以提高Python程序的性能。开发人员可以选择提高其代码速度。例如,一般建议是使用经过优化的Python内置或第三方例程,这些例程通常以C或Cython编写。此外,使用局部变量比使用全局变量更快,因此,在循环之前将全局变量复制到局部变量是一个好习惯。等等。

最后,总有可能用C,C ++或Cython编写自己的Python函数,从应用程序中调用它们并替换Python瓶颈例程。但这通常是一个极端的解决方案,实践中几乎没有必要。

使用Python循环时,特别是在进行大量迭代时,常常会出现性能问题。有许多有用的技巧可以改善代码并使之运行得更快,但这超出了本文的范围。

本文比较了按元素求和两个序列时几种方法的性能:

  • 使用while循环
  • 使用for循环
  • 将for循环用于列表推导
  • 使用第三方库 

    numpy

但是,性能并不是开发软件时唯一关心的问题。此外,根据《计算机编程艺术》中的Donald Knuth所说,“过早的优化是编程中所有(或至少其中大部分)邪恶的根源”。毕竟,正如蒂姆·彼得斯(Tim Peters)在《 Python Zen》中所说的,“可读性至关重要”。

问题陈述

我们将尝试按元素求和两个序列。换句话说,我们将采用两个大小相同的序列(列表或数组),并使用通过从输入中添加相应元素而获得的元素来创建第三个序列。

准备

我们将导入Python内置程序包random并生成一个列表r,其中包含100.000个伪随机数,范围从0到99(含):

import randomr = [random.randrange(100) for _ in range(100_000)]
           

我们还将使用第三方包

numpy

,因此我们将其导入:

import numpy as np
           

我们已经准备好出发了!

简单循环

首先让我们看一下一些简单的Python循环。

使用纯Python

我们将从两个具有1.000个元素的列表开始。整数变量n表示每个列表的长度。列表x和y是通过从r中随机选择n个元素获得的:

n = 1_000x, y = random.sample(r, n), random.sample(r, n)
           

让我们看看获取具有n个元素的新列表z所需的时间,每个元素是x和y中相应元素的总和。

我们将首先测试while循环的性能:

%%timeiti, z = 0, []while i < n:z.append(x[i] + y[i])i += 1
           

输出为:

每个循环160 µs±1.44 µs(平均±标准偏差,共运行7次,每个回路10000个)

请注意,的输出

timeit

取决于许多因素,并且每次可能都不同。

Python中的for循环针对这种情况进行了更好的优化,即遍历集合,迭代器,生成器等。让我们看看它是如何工作的:

%%timeitz = []for i in range(n):    z.append(x[i] + y[i])
           

输出为:

每个循环122 µs±188 ns(平均±标准偏差,共运行7次,每个循环10000个)

在这种情况下,for循环比while循环更快,但也更优雅。

列表推导与普通的for循环非常相似。它们适用于简单的情况(例如这种情况)。除了更紧凑之外,由于消除了一些开销,它们通常会稍微快一些:

%%timeitz = [x[i] + y[i] for i in range(n)
           

输出为:

每个回路87.2 µs±490 ns(平均±标准偏差,共运行7次,每个回路10000个)

请记住,当您需要循环时,不能在所有情况下都应用列表推导。一些更复杂的情况需要普通的for或while循环。

在NumPy中使用Python

numpy

是第三方Python库,通常用于数值计算。特别适合操纵数组。它提供了许多有用的例程来处理数组,但也允许编写紧凑而优雅的代码而没有循环。

实际上,循环以及其他对性能至关重要的操作是在

numpy

较低级别上实现的。

numpy

与纯Python代码相比,这可使例程更快。另一个优势是

numpy

处理变量和类型的方式。

首先让我们使用Python整数x和y的列表创建对应

numpy

的64位整数数组:

x_, y_ = np.array(x, dtype=np.int64), np.array(y, dtype=np.int64)
           

numpy

元素求和两个数组x_和y_就像x_ + y_一样容易。但是让我们检查一下性能:

%%timeitz = x_ + y_
           

输出为:

每个循环1.03 µs±5.09 ns(平均±标准偏差,共运行7次,每个循环1000000次)

这比我们使用列表推导时快了将近85倍。而且代码极其简单优雅。

numpy

数组可能是处理大型数组的更好选择。当数据更大时,性能优势通常会更大。

可能会更好。如果可以使用32位整数而不是64位整数,则在某些情况下可以节省内存和时间:

x_, y_ = np.array(x, dtype=np.int32), np.array(y, dtype=np.int32)
           

我们可以像以前一样添加这两个数组:

%%timeitz = x_ + y_
           

输出为:

每个循环814 ns±5.8 ns(平均±标准偏差,共7次运行,每个循环1000000次)

下表显示n较大时获得的结果,即10_000和100_000。在这种情况下,它们显示相同的关系,使用时甚至可以提高性能

numpy

嵌套循环

现在让我们比较嵌套的Python循环。

使用纯Python

我们将再次处理两个名为x和y的列表。它们每个都将包含100个内部列表,其中包含1.000个伪随机整数元素。因此,x和y实际上代表具有100行和1.000列的矩阵:

m, n = 100, 1_000x = [random.sample(r, n) for _ in range(m)]y = [random.sample(r, n) for _ in range(m)]
           

让我们看看使用两个嵌套的while循环添加它们的性能:

%%timeiti, z = 0, []while i < m:    j, z_ = 0, []    while j < n:        z_.append(x[i][j] + y[i][j])        j += 1    z.append(z_)    i += 1
           

输出为:

每个循环19.7 ms±271 µs(平均±标准偏差,共运行7次,每个循环100个)再次,我们可以使用嵌套的for循环来提高性能:

%%timeitz = []for i in range(m):    z_ = []    for j in range(n):         z_.append(x[i][j] + y[i][j])    z.append(z_)
           

输出为:

每个循环16.4 ms±303 µs(平均±标准偏差,运行7次,每个循环100个循环)在某些情况下,嵌套的for循环可用于列表推导,从而带来额外的好处:

%%timeitz = [[x[i][j] + y[i][j] for j in range(n)] for i in range(m)]
           

输出为:

每个循环12.1 ms±99.4 µs(平均±标准偏差,共运行7次,每个循环100个)

我们可以看到,在嵌套循环的情况下,列表理解要比普通的for循环要快,而for循环要比while快。

在这种情况下,每个列表中都有100.000(100×1.000)个整数元素。此示例比具有100.000元素和单个循环的示例稍慢。这是所有三种方法的结论(列表理解,普通for和while循环)。

在NumPy中使用Python

numpy

非常适合与多维数组一起使用。让我们使用x和y创建对应

numpy

的64位整数数组:

x_, y_ = np.array(x, dtype=np.int64), np.array(y, dtype=np.int64)
           

让我们检查一下性能:

%%timeitz = x_ + y_
           

输出为:

每个循环69.9 µs±909 ns(平均±标准偏差,共运行7次,每个循环10000个),这比列表理解的速度快173倍。但是如果我们使用32位整数,它可能会更快:

x_, y_ = np.array(x, dtype=np.int32), np.array(y, dtype=np.int32)
           

像以前一样进行性能检查:

%%timeitz = x_ + y_
           

输出为:

每个循环34.3 µs±44.6 ns(平均±标准偏差,共运行7次,每个循环10000个),这比64位整数快两倍。

结果汇总

下图总结了获得的结果:

for循环优化_Python中的循环比较和性能

结论

本文比较了按元素添加两个列表或数组时Python循环的性能。结果表明,列表理解比普通的for循环要快,而while循环则要快。在所有这三种情况下,简单循环都比嵌套循环快一点。

numpy

提供的例程和运算符可以大大减少代码量并提高执行速度。在处理一维和多维数组时特别有用。

请记住,此处得出的结论或结果之间的关系在所有情况下都不适用,无效或无用!提出它们是为了说明。处理效率低下的正确方法是发现瓶颈并执行自己的测试。

往期精彩链接

《使用Python进行图像处理和采集》第二版随机森林原理介绍与适用情况(综述篇)'建议收藏'

for循环优化_Python中的循环比较和性能

欢迎各位志同道合的同学老师添加微信群一起讨论!

for循环优化_Python中的循环比较和性能

继续阅读