一些示例的Python代码:
nums = [1,2,3]
start = timer()
for i in range(len(nums)):
print(nums[i])
end = timer()
print((end-start)) #computed to 0.0697546862831
start = timer()
print(nums[0])
print(nums[1])
print(nums[2])
end = timer()
print((end-start)) #computed to 0.0167170338524
我可以理解循环中会有一些额外的时间,因为i的值必须增加几次,但这两种不同方法的运行时间之间的差异似乎比我预期的要大很多。是否还有其他事情发生在引擎盖下,我没有考虑过?
答案 0 :(得分:4)
简短回答:不是,除非循环非常小。 for循环的开销很小,但是你这样做的效率很低。通过使用range(len(nums))
,您可以有效地创建另一个列表并迭代它,然后进行相同的索引查找。试试这个:
for i in nums:
print(i)
我的结果与预期一致:
>>> import timeit
>>> timeit.timeit('nums[0];nums[1];nums[2]', setup='nums = [1,2,3]')
0.10711812973022461
>>> timeit.timeit('for i in nums:pass', setup='nums = [1,2,3]')
0.13474011421203613
>>> timeit.timeit('for i in range(len(nums)):pass', setup='nums = [1,2,3]')
0.42371487617492676
使用更大的列表,循环的优势变得明显,因为通过索引访问元素的增量成本超过了循环的一次性成本:
>>> timeit.timeit('for i in nums:pass', setup='nums = range(0,100)')
1.541944980621338
timeit.timeit(';'.join('nums[%s]' % i for i in range(0,100)), setup='nums = range(0,100)')
2.5244338512420654
在python 3中,它比可索引列表更强调迭代器,差异更大:
>>> timeit.timeit('for i in nums:pass', setup='nums = range(0,100)')
1.6542046590038808
>>> timeit.timeit(';'.join('nums[%s]' % i for i in range(0,100)), setup='nums = range(0,100)')
10.331634456000756
答案 1 :(得分:2)
使用如此小的阵列,您可能首先测量噪音,然后调用range()
的开销。请注意,range
不仅必须将变量增加几次,还会创建一个保持其状态(当前值)的对象,因为它是一个生成器。函数调用和对象创建是你在第二个例子中付出的两件事,对于非常短的迭代,他们可能会使三个数组访问相形见绌。
基本上你的第二个代码片段循环展开,这是一种加速性能关键代码的可行且常用的技术。
答案 2 :(得分:0)
for loop
在任何情况下都有成本,而你写的那个成本特别高。这里有四个版本,使用timeit测量时间:
from timeit import timeit
NUMS = [1, 2, 3]
def one():
for i in range(len(NUMS)):
NUMS[i]
def one_no_access():
for i in range(len(NUMS)):
i
def two():
NUMS[0]
NUMS[1]
NUMS[2]
def three():
for i in NUMS:
i
for func in (one, one_no_access, two, three):
print(func.__name__ + ':', timeit(func))
以下是发现时间:
one: 1.0467438200000743
one_no_access: 0.8853238560000136
two: 0.3143197629999577
three: 0.3478466749998006
one_no_access
显示表达式range(len(NUMS))
的费用。
虽然python中的列表在内存中连续存放,但元素的随机访问位于O(1)
,解释两个更快。