python列表中的不连续切片

hel*_*ker 21 python performance list slice

我正在寻找一种实现这一目标的有效方法,我认为这是一种类似切片的操作:

>>> mylist = range(100)
>>>magicslicer(mylist, 10, 20)
[0,1,2,3,4,5,6,7,8,9,30,31,32,33,34,35,36,37,38,39,60,61,62,63......,97,98,99]
Run Code Online (Sandbox Code Playgroud)

这个想法是:切片得到10个元素,然后跳过 20个元素,然后得到下一个10,然后跳过20个,依此类推.

我认为如果可能的话我不应该使用循环,因为使用切片的原因是(我猜)在单个操作中有效地进行"提取".

谢谢阅读.

nco*_*lan 20

itertools.compress(2.7/3.1中的新功能)很好地支持像这样的用例,特别是当结合使用时itertools.cycle:

from itertools import cycle, compress
seq = range(100)
criteria = cycle([True]*10 + [False]*20) # Use whatever pattern you like
>>> list(compress(seq, criteria))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
Run Code Online (Sandbox Code Playgroud)

Python 2.7时序(相对于Sven的显式列表理解):

$ ./python -m timeit -s "a = range(100)" "[x for start in range(0, len(a), 30) for x in a[start:start+10]]"
100000 loops, best of 3: 4.96 usec per loop

$ ./python -m timeit -s "from itertools import cycle, compress" -s "a = range(100)" -s "criteria = cycle([True]*10 + [False]*20)" "list(compress(a, criteria))"
100000 loops, best of 3: 4.76 usec per loop
Run Code Online (Sandbox Code Playgroud)

Python 3.2时序(也与Sven的显式列表理解有关):

$ ./python -m timeit -s "a = range(100)" "[x for start in range(0, len(a), 30) for x in a[start:start+10]]"
100000 loops, best of 3: 7.41 usec per loop

$ ./python -m timeit -s "from itertools import cycle, compress" -s "a = range(100)" -s "criteria = cycle([True]*10 + [False]*20)" "list(compress(a, criteria))"
100000 loops, best of 3: 4.78 usec per loop
Run Code Online (Sandbox Code Playgroud)

可以看出,相对于2.7中的内联列表理解,它没有太大的区别,但是通过避免隐式嵌套范围的开销在3.2中有很大帮助.

如果目标是迭代结果序列而不是将其转换为完全实现的列表,则在2.7中也可以看到类似的差异:

$ ./python -m timeit -s "a = range(100)" "for x in (x for start in range(0, len(a), 30) for x in a[start:start+10]): pass"
100000 loops, best of 3: 6.82 usec per loop
$ ./python -m timeit -s "from itertools import cycle, compress" -s "a = range(100)" -s "criteria = cycle([True]*10 + [False]*20)" "for x in compress(a, criteria): pass"
100000 loops, best of 3: 3.61 usec per loop
Run Code Online (Sandbox Code Playgroud)

对于特别长的模式,可以使用类似的表达式替换模式表达式中的列表,chain(repeat(True, 10), repeat(False, 20))以便它永远不必在内存中完全创建.

  • 太好了!请注意,如果要跳过大量值,显式列表推导仍然会更快,因为`compress()`必须迭代所有值,而列表推导确实会跳过它们. (4认同)
  • 确实,虽然`compress()`方法的相反好处是它适用于任意迭代,甚至是不支持切片的迭代. (2认同)

Sve*_*ach 10

也许最好的方法是直截了当的方法:

def magicslicer(seq, take, skip):
    return [x for start in range(0, len(seq), take + skip)
              for x in seq[start:start + take]]
Run Code Online (Sandbox Code Playgroud)

我认为你不能避免循环.

编辑:由于这是标记为"性能",这里与模数解决方案进行比较a = range(100):

In [2]: %timeit [x for start in range(0, len(a), 30)
                   for x in a[start:start + 10]]
100000 loops, best of 3: 4.89 us per loop

In [3]: %timeit [e for i, e in enumerate(a) if i % 30 < 10]
100000 loops, best of 3: 14.8 us per loop
Run Code Online (Sandbox Code Playgroud)