对collections.deque使用切片表示法

Jon*_*han 42 python deque slice

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

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

片符号似乎并没有一起工作deque......

Sha*_*hin 59

import itertools
output = list(itertools.islice(q, 3, 7))
Run Code Online (Sandbox Code Playgroud)

例如:

>>> 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]
Run Code Online (Sandbox Code Playgroud)

这应该比迄今为止发布的其他解决方案更有效.证明?

[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
Run Code Online (Sandbox Code Playgroud)


Ros*_*ews 7

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

output = list(q)[3:6+1]
Run Code Online (Sandbox Code Playgroud)

  • 但它制作了两份`deque`(一份完整,一份部分)而不是一份部分副本。如果 `deque` 很短,那不是问题,但如果它很长,则可能会出现问题。 (2认同)
  • 诚然。但是,这并不是真正的“双端队列”用例。另一个解决方案为“范围”创建一个列表。我认为这就像“list(set(list))”黑客寻找列表的独特元素 - 优雅,漂亮,但可能不是最有效的。 (2认同)

mus*_*nte 6

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

为简单起见,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))
Run Code Online (Sandbox Code Playgroud)

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

注意:这仅对 Python <=2.* 有效。还值得注意的是,虽然自 python 2.0 以来已__getslice__弃用,但文档仍然报告了最新的 2.7 版本:

(但是,CPython 中的内置类型目前仍然实现__getslice__()。因此,在实现切片时,您必须在派生类中覆盖它。)

  • 自 Python 2.0 起,`__getslice__` 已被废弃:https://docs.python.org/2/reference/datamodel.html#object.__getslice__ (2认同)
  • @Zeromatiker 你是对的,谢谢你指出这一点。然而,文档中的注释对于 python 2 仍然有效(而且我们都知道,无论如何,即使在 2020 年中期,它仍然存在):“CPython 中的内置类型目前仍然实现 __getslice__()。因此,实现切片时,您必须在派生类中重写它。” (2认同)

mos*_*evi 6

您可以重写该 __getitem__方法并创建一个SliceableDequeusing 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)
        try:  # normal slicing
            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))
Run Code Online (Sandbox Code Playgroud)


小智 5

这是一个老问题,但对于任何未来的旅行者,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
Run Code Online (Sandbox Code Playgroud)

效果与islice直接使用相同,只是rotate跳过到起点的效率更高。另一方面,它还会临时修改双端队列,这可能是线程安全问题。