23

Вопрос по python, slice, deque – Как нарезать деку? [Дубликат]

This question already has an answer here:

Use slice notation with collections.deque 6 answers

Я изменил некоторый код, который использовал список, на использование deque. Я больше не могу нарезать его, так как я получаю сообщение об ошибке:

TypeError: sequence index must be integer, not 'slice'

Вот ответ, который показывает проблему.

>>> import collections
>>> d = collections.deque()
>>> for i in range(3):
...     d.append(i)
...
>>> d
deque([0, 1, 2])
>>> d[2:]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: sequence index must be integer, not 'slice'

Итак, есть ли обходной путь для поддержки разделения на запросы в Python?

@HunterMcMillen, спасибо за ваш комментарий. Я используюdeque для производительности (фактически в виде циклического буфера) и, следовательно, для копирования данных в список каждый цикл не идеален.

от Drew Noakes

@ thg435, спасибо за перекрестную ссылку. Я погуглил строку с ошибкой и ничего не нашел на SO, поэтому опубликовал этот новый вопрос. Там тоже есть хорошие идеи.

от Drew Noakes

отн:stackoverflow.com/questions/7064289/…

от georg
2 ответа
26

Пытаться

itertools.islice().

 deque_slice = collections.deque(itertools.islice(my_deque, 10, 20))

Индексирование вdeque требует каждый раз следовать связанному списку с самого начала, поэтомуislice() подход, пропуская элементы, чтобы добраться до начала среза, даст наилучшую возможную производительность (лучше, чем кодирование его как операции индексации для каждого элемента).

Вы могли бы легко написатьdeque подкласс, который делает это автоматически для вас.

class sliceable_deque(collections.deque):
    def __getitem__(self, index):
        if isinstance(index, slice):
            return type(self)(itertools.islice(self, index.start,
                                               index.stop, index.step))
        return collections.deque.__getitem__(self, index)

Обратите внимание, что вы не можете использовать отрицательные индексы или значения шага сislice, Можно обойти это, и, возможно, стоит сделать это, если вы используете подход подкласса. Для отрицательного старта или остановки вы можете просто добавить длину deque; для отрицательного шага вам нужноreversed() где-то там Я оставлю это как упражнение. :-)

Производительность извлечения отдельных элементов изdeque будет немного уменьшенif тест на ломтик. Если это проблема, вы можете использовать шаблон EAFP для некоторого улучшения - за счет того, что путь среза будет несколько менее производительным из-за необходимости обработки исключения:

class sliceable_deque(collections.deque):
    def __getitem__(self, index):
        try:
            return collections.deque.__getitem__(self, index)
        except TypeError:
            return type(self)(itertools.islice(self, index.start,
                                               index.stop, index.step))

Конечно, здесь есть дополнительный вызов функции по сравнению с обычнымdeque, так что если вы действительно заботитесь о производительности, вы действительно хотите добавить отдельныйslice() метод или тому подобное.

Error: User Rate Limit Exceeded

от 

Error: User Rate Limit Exceeded

от 

Error: User Rate Limit Exceeded__setitem__Error: User Rate Limit Exceeded__delitem__Error: User Rate Limit Exceededtype(self)(...)Error: User Rate Limit Exceededexcept:Error: User Rate Limit Exceededcollections.dequeError: User Rate Limit Exceededtry:Error: User Rate Limit ExceededsuperError: User Rate Limit Exceededdon'tError: User Rate Limit Exceeded

от 

Error: User Rate Limit Exceeded

от 
8

Если производительность является проблемой

рассмотрите метод прямого доступа / понимания, как предложено вэтот ответ, Это намного быстрее, чемislice на большие коллекции:

import timeit

setup = """
import collections, itertools
d = collections.deque(range(10000))
"""

print timeit.timeit('list(itertools.islice(d, 9000, 9010))', setup, number=10000)
## 0.631947040558
print timeit.timeit('[d[i] for i in range(9000, 9010)]', setup, number=10000)
## 0.0292208194733

Согласно комментарию @RaymondHettinger ниже, метод понимания лучше только тогда, когда срезы короткие. На более длинные ломтики,islice убедительно побеждает. Например, вот время для нарезки 10000 элементов из смещения 6000:

offset  length      islice       compr
 6000      10      400.496      46.611
 6000      50      424.600     183.988
 6000      90      432.277     237.894
 6000     130      441.289     352.383
 6000     170      431.299     404.596
 6000     210      456.405     546.503
 6000     250      448.895     575.995
 6000     290      485.802     778.294
 6000     330      483.704     781.703
 6000     370      490.904     948.501
 6000     410      500.011     875.807
 6000     450      508.213    1045.299
 6000     490      518.894    1010.203
 6000     530      530.887    1192.784
 6000     570      534.415    1151.013
 6000     610      530.887    1504.779
 6000     650      539.279    1486.802
 6000     690      536.084    1650.810
 6000     730      549.698    1454.687
 6000     770      564.909    1576.114
 6000     810      545.001    1588.297
 6000     850      564.504    1711.607
 6000     890      584.197    1760.793
 6000     930      564.480    1963.091
 6000     970      586.390    1955.199
 6000    1010      590.706    2117.491

Понимание делает первые несколько срезов очень быстро, но производительность резко падает с увеличением длины.islice медленнее на небольших кусочках, но его средняя скорость намного лучше.

Вот как я тестировал:

import timeit

size = 10000
repeats = 100

setup = """
import collections, itertools
d = collections.deque(range(%d))
""" % size

print '%5s\t%5s\t%10s\t%10s' % ('offset', 'length', 'islice', 'compr')

for offset in range(0, size - 2000, 2000):
    for length in range(10, 2000, 40):
        t1 = timeit.timeit('list(itertools.islice(d, %d, %d))' % (offset, offset + length), setup, number=repeats)
        t2 = timeit.timeit('[d[i] for i in range(%d, %d)]' % (offset, offset + length), setup, number=repeats)
        print '%5d\t%5d\t%10.3f\t%10.3f' % (offset, length, t1 * 100000, t2  * 100000)

Error: User Rate Limit ExceededreallyError: User Rate Limit Exceededi > len(d)//2Error: User Rate Limit Exceeded9000:10000Error: User Rate Limit Exceeded

от 

Error: User Rate Limit ExceededdequeError: User Rate Limit Exceeded

от 

Error: User Rate Limit Exceeded__getitem__Error: User Rate Limit Exceeded

от 

Error: User Rate Limit Exceeded

от 

Error: User Rate Limit Exceeded

от 

Похожие вопросы