2016-10-29 2 views
0

Многие из нас знают, что enumerate используется в ситуации, когда вы используете цикл for и вам нужно знать индекс. Однако он имеет свои минусы. Согласно моим тестам с модулем timeit, использование enumerate делает код 2х медленнее. Добавление этого набора кортежей делает его медленнее до 3x. Эти цифры могут быть достаточно быстрыми для любого программиста, но люди, имеющие дело с алгоритмами, знают, что каждый бит кода, который вы можете оптимизировать, имеет огромное преимущество. Теперь, на мой вопрос,Python for loop index, without enumerate

Примером такого использования будет необходимость поиска индексов нескольких элементов в list. Скажите, что нам нужно найти два элемента. Первые два решения, которые происходят со мной, как так:

x, y = 0, 0 
for ind, val in enumerate(lst): 
    if x and y: 
     break 
    if val == "a": 
     x = ind 
    elif val == "b": 
     y = ind 

Решение выше перебирает список, присвоить значения, чем сломаться, если два найден.

x = lst.index("a") 
y = lst.index("b") 

Это другое решение, которое я не хотел использовать, потому что оно оказалось действительно наивным. Он повторяет один и тот же список дважды, чтобы найти два элемента. Первое решение, делает это в одной итерации. Таким образом, по сложности, хотя мы делаем дополнительные задания в первом решении, он должен быть быстрее второго в более крупных списках. Но мое предположение не выполнено.

Вот код, я проверил производительность:https://codeshare.io/XfvGA

Второе решение было 2x 10 раз быстрее, чем первый, меняется с положением этих двух элементов. Есть несколько возможностей, которые это произойдет.

  • Существует оптимизация в методе index(), о котором я не знаю.
  • Назначения нижнего уровня, выполняемые в методе index(). Возможно использование кода на C++.
  • Условия и дополнительные задания в первом решении делают его медленнее, чем ожидалось.

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

Так что, если вам нужно, чтобы вы рассмотрели дела, которые я представил, чтобы уточнить, что здесь задается, вопрос может быть составлен таким образом. Мы знаем, что цикл Python for на самом деле является while, работающим в фоновом режиме (возможно, в C?). Таким образом, это означает, что индекс хранится по мере его увеличения где-то в памяти. Если бы был доступ к нему, это устранило бы стоимость вызова и распаковки enumerate. Мой вопрос:

Есть ли такой способ существует? Если нет, может быть сделано (почему, или почему нет)?

источники я использовал для получения дополнительной информации по этой теме:

Python speed

Python objects time complexity

Performance tips for Python

+0

Я не вижу оснований предполагать, что код, который имеет одиночный цикл (но который выполняет в два раза больше работы за проход), должен быть быстрее, чем два последовательных цикла, каждый из которых делает меньше работы за проход. Кроме того, обратите внимание, что одноточечная версия только коротких замыканий после обоих элементов была найдена. Если вы тестировали случайный ввод данных, это могло бы объяснить некоторые различия. –

+0

@JohnColeman Конечно, работа, выполняемая в петлях, решает производительность. Однако теоретически работа, выполняемая этими циклами, может быть одинаковой (возможно, не в Python, а как псевдокод) и единственной итерации быстрее. – Rockybilly

+0

Ваше утверждение о наличии ** индекса **, хранящегося где-то в памяти для каждого цикла 'for', также неверно. Протокол итератора просто должен вернуть значение или вызвать исключение 'StopIteration', когда вызывается' next() ', он явно свободен от требования поддерживать индекс в списке. Вот почему существует 'enumerate', чтобы имитировать эту функцию при переходе по фиксированному списку, когда это необходимо. –

ответ

1

Я не думаю, что enumerate проблема, чтобы доказать это вы возможно:

x, y = 0, 0 
for val in a: 
    if x and y: 
     break 
    if val == "a": 
     x = val 
    elif val == "b": 
     y = val 

Это не делает то же самое, что вы хотели в первую очередь (вы не получаете индекс), но если вы его испортите с помощью timeit, вы обнаружите, что разница не столь значительна, что означает, что enumerate не является источником (в моем случае это было 0,185-0,155 при выполнении вашего примера, так что это быстрее, но второе решение получило 0.055 на моем компьютере)

Причина, по которой lst.index быстрее, заключается в том, что она реализована на C.

Вы можете увидеть его исходный код здесь: https://svn.python.org/projects/python/trunk/Objects/listobject.c функция индекса называется listindex в этом файле и определяется как

static PyObject * listindex(PyListObject *self, PyObject *args) (я не мог найти способ, чтобы добавить ссылку непосредственно к функции)

+0

Вы правы, однако это не меняет того, что, если нам нужно найти несколько индексов, нам все равно нужно перебирать список более одного раза. Что является излишним, независимо от того, какой язык вы используете. – Rockybilly

0

Вы пытаетесь быть не-питоническим, который не закончится ужасно хорошо для вас. Если вам действительно нужно иметь доступную информацию о количестве итераторов, существует хорошо известный и оптимизированный способ сделать это: enumerate(). Если вам нужно найти элемент в списке, есть хорошо известный и оптимизированный способ сделать это: lst.index(). Как показала DorElias выше/ниже, enumerate не проблема, это то, что вы пытаетесь изобрести колесо с остальной частью своего цикла for. enumerate будет самым лучшим (самым быстрым, быстрым и т. Д.) Способом поддерживать счетчик итераций в любой ситуации , где количество итераций - это то, что вам нужно.

+0

Колесо повторно изобретено, чтобы визуализировать то, что требуется, чтобы найти, применимо ли подобное поведение в терминах Python. Кроме того, я согласен с тобой. – Rockybilly