简体   繁体   English

Python'for'循环的更好方法

[英]A better way for a Python 'for' loop

We all know that the common way of executing a statement a certain number of times in Python is to use a for loop. 我们都知道在Python中执行语句一定次数的常用方法是使用for循环。

The general way of doing this is, 这样做的一般方法是,

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

I believe nobody will argue that the code above is the common implementation, however there is another option. 我相信没有人会争辩说上面的代码是常见的实现,但是还有另一种选择。 Using the speed of Python list creation by multiplying references. 通过乘以引用来使用Python列表创建的速度。

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

There is also the old while way. 也有老的while方式。

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

I tested the execution times of these approaches. 我测试了这些方法的执行时间。 Here is the code. 这是代码。

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

I would not initiate the subject if there was a small difference, however it can be seen that the difference of speed is 100%. 如果存在小的差异,我不会发起主题,但是可以看出速度的差异是100%。 Why does not Python encourage such usage if the second method is much more efficient? 如果第二种方法效率更高,为什么Python不鼓励这种用法呢? Is there a better way? 有没有更好的办法?

The test is done with Windows 10 and Python 3.6 . 该测试使用Windows 10Python 3.6完成

Following @Tim Peters' suggestion, 按照@Tim Peters的建议,

.
.
.
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

Which offers a much better way, and this pretty much answers my question. 这提供了一个更好的方式,这几乎回答了我的问题。

Why is this faster than range , since both are generators. 为什么这比range更快,因为两者都是发电机。 Is it because the value never changes? 是因为价值永远不变吗?

Using 运用

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

is the non-obvious way of getting the best of all worlds: tiny constant space requirement, and no new objects created per iteration. 是获得最佳世界的非显而易见的方法:微小的恒定空间要求,并且每次迭代都不会创建新对象。 Under the covers, the C code for repeat uses a native C integer type (not a Python integer object!) to keep track of the count remaining. 在封面下, repeat的C代码使用本机C整数类型(不是Python整数对象!)来跟踪剩余的计数。

For that reason, the count needs to fit in the platform C ssize_t type, which is generally at most 2**31 - 1 on a 32-bit box, and here on a 64-bit box: 出于这个原因,计数需要适合平台C ssize_t类型,在32位盒子上通常最多2**31 - 1 ,这里是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)

Which is plenty big for my loops ;-) 这对我的循环来说很重要;-)

The first method (in Python 3) creates a range object, which can iterate through the range of values. 第一种方法(在Python 3中)创建一个范围对象,它可以遍历值范围。 (It's like a generator object but you can iterate through it several times.) It doesn't take up much memory because it doesn't contain the entire range of values, just a current and a maximum value, where it keeps increasing by the step size (default 1) until it hits or passes the maximum. (它就像一个生成器对象,但你可以多次遍历它。)它不会占用太多内存,因为它不包含整个值范围,只包含当前值和最大值,它不断增加步长(默认为1),直到达到或超过最大值。

Compare the size of range(0, 1000) to the size of list(range(0, 1000)) : Try It Online! range(0, 1000)的大小与list(range(0, 1000))的大小list(range(0, 1000))在线试用! . The former is very memory efficient; 前者非常有效; it only takes 48 bytes regardless of the size, whereas the entire list increases linearly in terms of size. 无论大小如何,它只需要48个字节,而整个列表在大小方面呈线性增长。

The second method, although faster, takes up that memory I was talking about in the past one. 第二种方法虽然速度更快,却占用了我过去谈论的内存。 (Also, it seems that although 0 takes up 24 bytes and None takes 16, arrays of 10000 of each have the same size. Interesting. Probably because they're pointers) (另外,似乎虽然0占用24个字节而None占用16个,但每个10000个数组的大小相同。有趣。可能因为它们是指针)

Interestingly enough, [0] * 10000 is smaller than list(range(10000)) by about 10000, which kind of makes sense because in the first one, everything is the same primitive value so it can be optimized. 有趣的是, [0] * 10000小于list(range(10000))大约10000,这是有道理的,因为在第一个中,所有内容都是相同的原始值,因此可以进行优化。

The third one is also nice because it doesn't require another stack value (whereas calling range requires another spot on the call stack), though since it's 6 times slower, it's not worth that. 第三个也很好,因为它不需要另一个堆栈值(而调用range需要调用堆栈上的另一个点),但由于它慢了6倍,所以不值得。

The last one might be the fastest just because itertools is cool that way :PI think it uses some C-library optimizations, if I remember correctly. 最后一个可能是最快的,因为itertools很酷:PI认为它使用了一些C库优化,如果我没记错的话。

The first two methods need to allocate memory blocks for each iteration while the third one would just make a step for each iteration. 前两种方法需要为每次迭代分配内存块,而第三种方法只需为每次迭代创建一个步骤。

Range is a slow function, and I use it only when I have to run small code that doesn't require speed, for example, range(0,50) . 范围是一个缓慢的函数,只有当我必须运行不需要速度的小代码时才使用它,例如, range(0,50) I think you can't compare the three methods; 我想你无法比较这三种方法; they are totally different. 他们是完全不同的。

According to a comment below, the first case is only valid for Python 2.7, in Python 3 it works like xrange and doesn't allocate a block for each iteration. 根据下面的评论,第一种情况仅适用于Python 2.7,在Python 3中它的工作方式与xrange类似,并且不为每次迭代分配一个块。 I tested it, and he is right. 我测试了,他是对的。

This answer provides a loop construct for convenience. 这个答案提供了一个方便的循环结构。 For additional background about looping with itertools.repeat look up Tim Peters' answer above , Alex Martelli's answer here and Raymond Hettinger's answer here . 有关使用循环的其他背景itertools.repeat查找添彼得斯的回答以上 ,亚历克斯·马尔泰利的答案在这里和雷蒙德赫廷杰的答案在这里

# loop.py

"""
Faster for-looping in CPython for cases where intermediate integers
from `range(x)` are not needed.

Example Usage:
--------------

from loop import loop

for _ in loop(10000):
    do_something()

# or:

results = [calc_value() for _ in loop(10000)]
"""

from itertools import repeat
from functools import partial

loop = partial(repeat, None)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 有没有更好的方法来替换 python 中的“for”循环? - Is there a better way to replace the "for" loop in python? 级联FOR循环输出是否有更好的Python 3方式 - Cascading FOR loop outputs is there a better python 3 way 有没有更好的循环代码的方法? - Is there a better way to loop code? 是否有更好或更优雅的方式来处理python中的字典循环异常? - is there a better or more elegant way to handling dictionary loop exception in python? 一种更好的方式来读取文本的子字符串而无需循环/ python - a better way to read in the substrings of a text without loop / python 知道在Python中的for循环中是否还有任何要迭代的元素的更好方法 - Better way to know if there are any elements left to iterate in for loop in Python 提高iteritems的性能或更好的遍历python字典的方法 - improve iteritems performance or a better way to loop through python dictionary Python,更好的编码方式。 使用循环数组? - Python, Better way of coding. Using Loop Array? 在python中不使用for循环打印网格的更好方法 - Better way to print a grid without using a for loop in python 是否有更好的方法仅使用 while 循环在 python 中打印此模式? - Is there a better way to print this pattern in python using while loop only?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM