web-dev-qa-db-ja.com

collections.dequeでスライス表記を使用する

次のdequeからアイテム3..6を効率的に、エレガントに、Pythonで、変更せずにどのように抽出しますか。

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

スライス表記deque...では機能しないようです...

47
Jonathan
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
55
Shawn Chin

私はこれが好きです、それは短くてとても読みやすいです:

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

より良いフォーマットを提供するために、これを新しい答えとして追加します。

簡単にするために、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))

これはsetting新しいスライスをサポートしませんが、同じ概念を使用して独自のカスタム__setslice__メソッドを実装できます。

2
musicamante

これは古い質問ですが、今後の旅行者のために、Python docsは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が開始点までスキップするのに効率的です。一方、それはまた、一時的に両端キューを変更しますが、これはスレッドセーフの問題となる可能性があります。

1
MattyV

__getitem__メソッドを使用し、SliceableDequeを使用してisliceを作成します。

Edgeのケースがあります。検討する必要があります(たとえば、負のスライスの使用は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
moshevi