2016-12-10 2 views
-1

Контекст

Это общий вопрос об эффективности. У меня есть список, и мне нужен последовательный run/sublist из списка. Как правило, это делается с помощью кусочка:Python 3.5: slice vs islice vs alternatives? Сравнение эффективности

my_list[start:end] 

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

islice - альтернатива, которая вместо этого делает итератор. Так как я забочусь только о том, все значения в одном, а не переборе над ними, у меня будет набирать ролях:

list(islice(my_list, start, end)) 

Фоновая работа

Чтобы сделать некоторые сравнения я случайно нарезанные/isliced ​​10 раз в списках увеличения размера от 1 до 10000:

is_vals = [] 
s_vals = [] 
for l in range(1, 10000): 

    my_list = [random.random() for k in range(l)] 

    for p in range(10): 
     i = random.randint(0, l) 
     j = random.randint(0, l) 

     if i < j: 

      start_time = time.clock() 
      list(islice(my_list, i, j)) 
      is_vals.append(time.clock() - start_time) 
      start_time = time.clock() 
      my_list[i:j] 
      s_vals.append(time.clock() - start_time) 

     else: 
      start_time = time.clock() 
      list(islice(my_list, j, i)) 
      is_vals.append(time.clock() - start_time) 
      start_time = time.clock() 
      my_list[j:i] 
      s_vals.append(time.clock() - start_time) 

print(statistics.mean(is_vals) - statistics.mean(s_vals)) 

, что я обнаружил, что кусочек еще быстрее, с той разницей между Ислицей и ломтиком быть 2.99e-05.

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

Вопрос

есть более эффективный способ, чем срез, чтобы получить подряд запустить/подсписок в списке?

Бонус: есть ли способ более или менее ввести тип списка/кортеж в срез? например поверните [i, j] в i: j?

+1

Таким образом, оба метода создают новый объект списка. Зачем вообще смотреть на 'islice()' ** **, так как он может только ввести больше сложности? 'list()' вызывает 'iter()' на итераторе (который в этом случае возвращает 'self'). 'islice()' должен вызывать 'iter()' в списке (возвращающий объект итератора списка). Все вокруг, гораздо больше работы. –

+0

Включение значения старта и остановки в срез, который вы можете сделать с помощью 'slice (i, j)'. Об этом уже говорилось в другом месте, см. [Как использовать двоеточие (:) в переменной] (// stackoverflow.com/q/40531795) или [Практическое руководство средой Python, я знаю кусочек Python, но как я могу использовать встроенные функции, в slice-объекте для этого?] (// stackoverflow.com/q/3911483) –

+0

@MartijnPieters, следовательно, вопрос, я ищу альтернативу, и, возможно, новый список без ссылок на оригинал мог бы быть быстрее ... – SumNeuron

ответ

1

Вы не можете победить mylist[start:stop] в скорости, нет. Нет, если вы хотите создать новый объект списка , содержащий те же элементы из смежной области входного списка.

Это потому, что реализация типа list имеет прямой доступ ко внутреннему хранилищу для объекта списка. Вы не можете получить доступ к этим элементам быстрее извне.

Используйте только итераторы, когда важна эффективность памяти. Итераторы добавляют накладные расходы скорости итерации, они обычно не быстрее. В этом случае выражение list(islice(my_list, start, stop)) будет делать следующую работу:

  1. Создать объект списка итератора для my_list; это приведет к элементам от my_list, когда вы перебираете его.
  2. создать новый объект итератора; это пропустит start элементов из итератора списка, а затем произведет значения, пока не достигнет индекса stop.
  3. производят итератор от объекта итератора . В этом случае это будет только повторное использование одного и того же объекта, но это все равно отдельный (C) вызов функции.
  4. создает новый объект списка из всех элементов, которые дает объект итератора, созданный на шаге 3.

mylist[start:stop] вызова с другой стороны, делает только это:

  1. вызова mylist.__getitem__(slice(start, stop)). Этот метод напрямую создает новый объект списка с теми же элементами, которые скопировали его внутренний массив непосредственно в новый массив объектов списка.
+0

просто nitpick: он не обязательно должен быть смежным, даже если стандартная нотация «шаг» должна быть самой быстрой. – MSeifert

+0

@MSeifert: да, если есть шаг, он не соприкасается; Я просто сосредоточился на этом вопросе. Впоследствии я сделаю некоторое редактирование. –

0
import random 
import time 
from itertools import islice 
import statistics 

l = 1000000 
is_vals, s_vals = [], [] 
my_list = [random.random() for _ in range(l)] 
for p in range(10): 
    i = random.randint(0, l//3) 
    j = random.randint(l-l//3, l) 

    start_time = time.clock() 
    sum1 = 0 
    for k in islice(my_list, i, j): 
     sum1 += k 
    is_vals.append(time.clock() - start_time) 
    start_time = time.clock() 
    sum2 = 0 
    for k in my_list[i:j]: 
     sum2 += k 
    s_vals.append(time.clock() - start_time) 
    assert sum1 == sum2 

print(is_vals) 
print(s_vals) 
print(statistics.mean(is_vals)-statistics.mean(s_vals)) 

Это показывает Ислице немного быстрее, чем срез. Это потому, что интерпретатор Python создает новый список (my_list [я: у]), а затем итерацию над ним в линии

for k in my_list[i:j]: 

, тогда как в линии

for k in islice(my_list, i, j): 

это не создает новый список и непосредственно выполняет итерацию по my_list от i-го до j-го индексов. Однако, когда вы пишете

list(islice(my_list, i, j)) 

новый список также создан, поэтому вы не видите никаких преимуществ перед срезом.

Смежные вопросы