Python for循环更好的方法

我们都知道在Python中执行语句的常用方法是使用for循环。

这样做的一般方法是,

 # I am assuming iterated list is redundant. # Just the number of execution matters. for _ in range(count): pass 

我相信没有人会争辩说上面的代码是常见的实现,但是还有另一种select。 通过乘以引用来创buildPython列表创build的速度。

 # Uncommon way. for _ in [0] * count: pass 

也有旧的方式。

 i = 0 while i < count: i += 1 

我testing了这些方法的执行时间。 这是代码。

 import timeit repeat = 10 total = 10 setup = """ count = 100000 """ test1 = """ for _ in range(count): pass """ test2 = """ for _ in [0] * count: pass """ test3 = """ i = 0 while i < count: i += 1 """ print(min(timeit.Timer(test1, setup=setup).repeat(repeat, total))) print(min(timeit.Timer(test2, setup=setup).repeat(repeat, total))) print(min(timeit.Timer(test3, setup=setup).repeat(repeat, total))) # Results 0.02238852552017738 0.011760978361696095 0.06971727824807639 

如果差别不大,我不会提出这个问题,但是可以看出,速度的差别是%100。 所以我的问题是,为什么Python不鼓励这种用法,如果第二种方法更有效率? 有没有更好的办法 ?

testing用Windows 10Python 3.6完成

编辑1:继@蒂姆·彼得斯的build议

 . . . test4 = """ for _ in itertools.repeat(None, count): pass """ print(min(timeit.Timer(test1, setup=setup).repeat(repeat, total))) print(min(timeit.Timer(test2, setup=setup).repeat(repeat, total))) print(min(timeit.Timer(test3, setup=setup).repeat(repeat, total))) print(min(timeit.Timer(test4, setup=setup).repeat(repeat, total))) # Gives 0.02306803115612352 0.013021619340942758 0.06400113461638746 0.008105080015739174 

哪个提供了更好的方法,这几乎可以解答我的问题。 谢谢你这么快的回答,如果在提问的时候我自己的研究部分不够用了,不好意思。

你能解释为什么这比range更快,因为两者都是发电机。 这是因为价值从不改变?

只是把我的评论“答案”:

 for _ in itertools.repeat(None, count) do something 

是获得最佳世界的非显而易见的方式:微小的恒定空间需求,并且每次迭代不会创build新的对象。 在封面之下, repeat的C代码使用本地C整数types(不是Python整数对象!)来跟踪剩余的计数。

出于这个原因,计数需要适合平台C ssize_ttypes,这通常在32位盒子上是2**31 - 1 ssize_t ,而在64位盒子上是这样的:

 >>> itertools.repeat(None, 2**63) Traceback (most recent call last): ... OverflowError: Python int too large to convert to C ssize_t >>> itertools.repeat(None, 2**63-1) repeat(None, 9223372036854775807) 

这对我的循环来说是足够大的;-)

第一种方法(在Python 3中)创build一个范围对象,它可以迭代值的范围。 (它就像一个生成器对象,但是可以迭代多次)。它不占用太多的内存,因为它不包含整个范围的值,只是一个当前值和一个最大值,步长(默认1),直到它达到或超过最大值。

range(0, 1000)的大小range(0, 1000)list(range(0, 1000))大小list(range(0, 1000)) : 在线试用! 。 前者非常有记忆效率; 无论大小如何,只需要48个字节,而整个列表在大小上线性增加。

第二种方法虽然速度更快,但却占用了我过去所说的内存。 (另外,虽然0占用了24个字节,而None占用了16个,但是每个数组有10000个都有相同的大小,有趣的可能是因为它们是指针)

有趣的是, [0] * 10000小于list(range(10000))大约10000,这是有道理的,因为在第一个中,所有东西都是相同的原始值,因此可以优化。

第三个也不错,因为它不需要另一个堆栈值(而调用range需要调用堆栈上的另一个地方),但是因为它慢了6倍,所以不值得。

最后一个可能是最快的,因为itertools是这么酷:PI认为它使用了一些C库优化,如果我没有记错的话。

前两个方法需要为每个迭代分配内存块,而第三个方法只是为每个迭代做一个步骤。

范围是一个缓慢的function,我只有在需要运行不需要速度的小代码range(0,50)例如range(0,50)时才使用它。 我认为你不能比较3种方法,它们是完全不同的。

更新

根据下面的评论,第一种情况只对Python 2.7有效,在python 3中它像xrange一样工作,并且不为每次迭代分配块。 经过testing,他是对的。