[在Python中通过切片操作进行迭代的效率如何?并且,如果不可避免地要有切片,那么有替代方法吗?
我知道列表上的切片操作为O(k),其中k是切片的大小。
x[5 : 5+k] # O(k) copy operation
但是,当遍历列表的一部分时,我发现执行此操作的最简洁(也是最Python化的方法?)(无需求助于索引):
for elem in x[5 : 5+k]:
print elem
但是我的直觉是,这仍然会导致子列表的复制,而不是简单地遍历现有列表。
您可以使用itertools.islice
从列表中获取切片式迭代器:
示例:
>>> from itertools import islice
>>> lis = range(20)
>>> for x in islice(lis, 10, None, 1):
... print x
...
10
11
12
13
14
15
16
17
18
19
[@ user2357112指出,islice
的性能取决于切片的起点和可迭代的大小,几乎在所有情况下,正常切片都将很快,因此应首选。以下是一些时序比较:
对于大列表 islice
在切片的开始点小于列表大小的一半时稍快或等于普通切片,对于较大的索引,普通切片是明显的赢家。
>>> def func(lis, n):
it = iter(lis)
for x in islice(it, n, None, 1):pass
...
>>> def func1(lis, n):
#it = iter(lis)
for x in islice(lis, n, None, 1):pass
...
>>> def func2(lis, n):
for x in lis[n:]:pass
...
>>> lis = range(10**6)
>>> n = 100
>>> %timeit func(lis, n)
10 loops, best of 3: 62.1 ms per loop
>>> %timeit func1(lis, n)
1 loops, best of 3: 60.8 ms per loop
>>> %timeit func2(lis, n)
1 loops, best of 3: 82.8 ms per loop
>>> n = 1000
>>> %timeit func(lis, n)
10 loops, best of 3: 64.4 ms per loop
>>> %timeit func1(lis, n)
1 loops, best of 3: 60.3 ms per loop
>>> %timeit func2(lis, n)
1 loops, best of 3: 85.8 ms per loop
>>> n = 10**4
>>> %timeit func(lis, n)
10 loops, best of 3: 61.4 ms per loop
>>> %timeit func1(lis, n)
10 loops, best of 3: 61 ms per loop
>>> %timeit func2(lis, n)
1 loops, best of 3: 80.8 ms per loop
>>> n = (10**6)/2
>>> %timeit func(lis, n)
10 loops, best of 3: 39.2 ms per loop
>>> %timeit func1(lis, n)
10 loops, best of 3: 39.6 ms per loop
>>> %timeit func2(lis, n)
10 loops, best of 3: 41.5 ms per loop
>>> n = (10**6)-1000
>>> %timeit func(lis, n)
100 loops, best of 3: 18.9 ms per loop
>>> %timeit func1(lis, n)
100 loops, best of 3: 18.8 ms per loop
>>> %timeit func2(lis, n)
10000 loops, best of 3: 50.9 us per loop #clear winner for large index
>>> %timeit func1(lis, n)
对于小列表在几乎所有情况下,普通切片比islice
快。
>>> lis = range(1000)
>>> n = 100
>>> %timeit func(lis, n)
10000 loops, best of 3: 60.7 us per loop
>>> %timeit func1(lis, n)
10000 loops, best of 3: 59.6 us per loop
>>> %timeit func2(lis, n)
10000 loops, best of 3: 59.9 us per loop
>>> n = 500
>>> %timeit func(lis, n)
10000 loops, best of 3: 38.4 us per loop
>>> %timeit func1(lis, n)
10000 loops, best of 3: 33.9 us per loop
>>> %timeit func2(lis, n)
10000 loops, best of 3: 26.6 us per loop
>>> n = 900
>>> %timeit func(lis, n)
10000 loops, best of 3: 20.1 us per loop
>>> %timeit func1(lis, n)
10000 loops, best of 3: 17.2 us per loop
>>> %timeit func2(lis, n)
10000 loops, best of 3: 11.3 us per loop
去普通切片。
用途:
for elem in x[5 : 5+k]:
这是Pythonic!在您完成代码的[[profiled并确定这是一个瓶颈之前,请不要更改它-尽管我怀疑您是否会发现它是瓶颈的主要根源。
In [30]: x = range(100)
In [31]: k = 90
In [32]: %timeit x[5:5+k]
1000000 loops, best of 3: 357 ns per loop
In [35]: %timeit list(IT.islice(x, 5, 5+k))
100000 loops, best of 3: 2.42 us per loop
In [36]: %timeit [x[i] for i in xrange(5, 5+k)]
100000 loops, best of 3: 5.71 us per loop
副本。因此,即使
就内存而言,这并不像您想象的那么糟糕。x[5: 5+k]
是x
的一部分的shallow
x
中的对象很大,x[5: 5+k]
也会创建一个包含k个元素的新列表,这些元素引用的对象与x
中的same对象相同。因此,您只需要额外的内存就可以创建一个列表,其中包含对现有对象的k个引用。那可能不会成为任何内存问题的根源。for i in xrange(5, 5+k):
print x[i]
授予:它看起来像是非Python的,但从不浪费额外内存的意义上讲,它比创建新的切片更有效。一种替代方法是使用迭代器,如@AshwiniChaudhary的答案所示。
>>> timeit.timeit('l[50:100]', 'import collections; l=range(150)')
0.46978958638010226
>>> timeit.timeit('for x in slice: pass',
'import collections; l=range(150); slice=l[50:100]')
1.2332711270150867
您可以尝试使用xrange
遍历索引,但是考虑到检索列表元素所需的时间,它比切片要慢。即使您跳过该部分,它也不会超出切片效果:
>>> timeit.timeit('for i in xrange(50, 100): x = l[i]', 'l = range(150)') 4.3081963062022055 >>> timeit.timeit('for i in xrange(50, 100): pass', 'l = range(150)') 1.675838213385532
请勿为此使用
itertools.islice
!将从头开始遍历您的列表,而不是跳至__getitem__
所需的值。以下是一些时序数据,显示了其性能如何取决于分片的起始位置:>>> timeit.timeit('next(itertools.islice(l, 9, None))', 'import itertools; l = r ange(1000000)') 0.5628290558478852 >>> timeit.timeit('next(itertools.islice(l, 999, None))', 'import itertools; l = range(1000000)') 6.885294697594759
这里islice
输给了常规切片:
>>> timeit.timeit('for i in itertools.islice(l, 900, None): pass', 'import itert ools; l = range(1000)') 8.979957560911316 >>> timeit.timeit('for i in l[900:]: pass', 'import itertools; l = range(1000)') 3.0318417204211983
这是在Python 2.7.5上,以防任何更高版本添加特定于列表的优化。
这是我想告诉你的事情:
i = 5 ## which is the initial value
f = 5 + k ## which will be the final index
while i < f:
print(x[i])
i += 1
我假设这个函数可以在5个小时内完成操作(因为相当于python的loop可以运行大约10个小时),因为我告诉过那个大k,因为它只需一次从5变为10000000000005!每次使用'xrange()'的'range()'甚至切片本身(如上所述),都会使程序仅执行20000000000000次迭代,这可能导致更长的执行时间。 (据我了解,使用生成器方法将返回一个可迭代对象,该对象需要首先完全运行生成器,并且需要花两次时间才能完成工作;一个用于生成器本身,另一个用于“ for”循环))>编辑:
在python 3中,生成器方法/对象不需要首先运行就可以为for循环创建可迭代对象
n
的顺序排列,其中n
是切片的起点。如果n
很大,那将是一个问题。后续结论(“ Go for normal slices]