Use slice notation with collections.deque

给你一囗甜甜゛ 提交于 2019-11-27 19:59:47
import itertools
output = list(itertools.islice(q, 3, 7))

For example:

>>> 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]

This should be more efficient the the other solutions posted so far. Proof?

[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

I would prefer this, it's shorter so easier to read:

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

I'd add this as a new answer, to provide better formatting.

For simplicity, Shawn's answer is perfect, but if you often need to get a slice from dequeue, you might prefer to subclass it and add a __getslice__ method.

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))

This won't support setting a new slice, but you can implement your own custom __setslice__ method using the same concept.

you can override the __getitem__ method and create a SliceableDeque using islice.

There are edge cases, you should consider (for example using negative slices doesn't work with islice).

here is what I have been using:

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))

This is an old question, but for any future travelers, the Python docs explicitly recommend using rotate for this:

The rotate() method provides a way to implement deque slicing and deletion.

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

An implementation is relatively simple:

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

Effectively the same as using islice directly, except that rotate is more efficient for skipping through to the starting point. On the other hand, it also temporarily modifies the deque, which could be a threadsafety concern.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!