对collections.deque使用切片表示法

问题描述 投票:39回答:6

如何从以下deque高效,优雅和蟒蛇地提取物品3..6而不改变它:

from collections import deque
q = deque('',maxlen=10)
for i in range(10,20):
    q.append(i)

slice notation似乎不适用于deque ......

python slice deque
6个回答
55
投票
import itertools
output = list(itertools.islice(q, 3, 7))

例如:

>>> import collections, itertools
>>> q = collections.deque(xrange(10, 20))
>>> q
deque([10, 11, 12, 13, 14, 15, 16, 17, 18, 19])
>>> list(itertools.islice(q, 3, 7))
[13, 14, 15, 16]

这应该比目前发布的其他解决方案更有效。证明?

[me@home]$ SETUP="import itertools,collections; q=collections.deque(xrange(1000000))"

[me@home]$ python -m timeit  "$SETUP" "list(itertools.islice(q, 10000, 20000))"
10 loops, best of 3: 68 msec per loop

[me@home]$ python -m timeit "$SETUP" "[q[i] for i in  xrange(10000, 20000)]"
10 loops, best of 3: 98.4 msec per loop

[me@home]$ python -m timeit "$SETUP" "list(q)[10000:20000]"
10 loops, best of 3: 107 msec per loop

4
投票

我更喜欢这个,它更短,更容易阅读:

output = list(q)[3:6+1]

3
投票
output = [q[i] for i in range(3,6+1)]

2
投票

我将此添加为新答案,以提供更好的格式。

为简单起见,Shawn的答案是完美的,但如果您经常需要从dequeue获得切片,您可能更喜欢将其子类化并添加__getslice__方法。

from collections import deque
from itertools import islice
class deque_slice(deque):
    def __new__(cls, *args):
        return deque.__new__(cls, *args)
    def __getslice__(self, start, end):
        return list(islice(self, start, end))

这不支持设置新切片,但您可以使用相同的概念实现自己的自定义__setslice__方法。


1
投票

你可以覆盖__getitem__方法并使用SliceableDeque创建一个islice

有边缘情况,你应该考虑(例如使用负片不适用于islice)。

这是我一直在使用的:

import itertools
from collections import deque

class SliceableDeque(deque):
    def __getitem__(self, s):
        try:
            start, stop, step = s.start or 0, s.stop or sys.maxsize, s.step or 1
        except AttributeError:  # not a slice but an int
            return super().__getitem__(s)
        else:
            try:
                return list(itertools.islice(self, start, stop, step))
            except ValueError:  # incase of a negative slice object
                length = len(self)
                start, stop = length + start if start < 0 else start, length + stop if stop < 0 else stop
                return list(itertools.islice(self, start, stop, step))

1
投票

这是一个老问题,但对于任何未来的旅行者,Python文档明确建议使用rotate

rotate()方法提供了一种实现双端切片和删除的方法。

https://docs.python.org/2/library/collections.html

实现相对简单:

def slice_deque(d, start, stop, step):
    d.rotate(-start)
    slice = list(itertools.islice(d, 0, stop-start, step))
    d.rotate(start)
    return slice

有效地与直接使用islice相同,除了rotate更有效地跳过起点。另一方面,它也暂时修改了双端队列,这可能是一个安全问题。

© www.soinside.com 2019 - 2024. All rights reserved.