питонический способ связать элементы списка с их индексами

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

Я могу сделать это так:

>>> t = (5,6,7)
>>> d = dict(zip(t, range(len(t))))
>>> d
{5: 0, 6: 1, 7: 2}

это неплохо, но ищу что-то более элегантное.

Я столкнулся со следующим, но он делает противоположное тому, что мне нужно:

>>> d = dict(enumerate(t))
>>> d
{0: 5, 1: 6, 2: 7}

Пожалуйста, поделитесь своими решениями,
Спасибо

ИЗМЕНИТЬ: Python 2.6.4

Для списков, содержащих 1000 элементов, версия dict (zip) является самой быстрой, версии генератора и понимания списка практически идентичны, и они примерно в 1,5 раза медленнее, а функциональная карта (перевернутая) значительно медленнее.

$ python -mtimeit -s "t = range (int (1e3))" "d = dict (zip (t, range (len (t))))"
1000 циклов, максимум 3: 277 мксек на цикл.

$ python -mtimeit -s "t = range (int (1e3))" "d = dict ([(y, x) for x, y in enumerate (t)])"
1000 циклов, лучшее из 3: 426 мксек на цикл

$ python -mtimeit -s "t = range (int (1e3))" "d = dict ((y, x) for x, y in enumerate (t))"
1000 циклов, лучшее из 3: 437 мкс. за петлю

$ python -mtimeit -s "t = range (int (1e3))" "d = dict (map (reversed, enumerate (t)))"
100 циклов, лучшее из 3: 3,66 мс на цикл.

Я пробовал проводить одни и те же тесты как для более длинных, так и для более коротких списков (1e2, 1e4, 1e5), и время на цикл линейно масштабируется с длиной списка.

Может ли кто-нибудь измерить версию Py 2.7+?


person AnalyticsBuilder    schedule 14.05.2010    source источник
comment
Мне любопытно - какая из реализаций быстрее? Кстати, Чуи, какую версию Python ты используешь?   -  person Hamish Grubijan    schedule 14.05.2010


Ответы (5)


Вы можете использовать понимание списка (или генератор, в зависимости от вашей версии Python), чтобы выполнить простую замену на месте для вашего второго примера.


Используя понимание списка:

d = dict([(y,x) for x,y in enumerate(t)])

Использование выражения генератора (Python 2.4 и выше):

d = dict((y,x) for x,y in enumerate(t))
person kibibu    schedule 14.05.2010
comment
Вам не нужен []. dict отлично работает с выражением генератора (сохраняет создание промежуточного списка) - person John La Rooy; 14.05.2010
comment
Ага, поэтому я и написал в зависимости от вашей версии python. Генераторы существуют уже давно (с версии 2.4), поэтому я включу оба - person kibibu; 14.05.2010
comment
@ J.F. Себастьян: В развернутых системах, разработанных до 2004 года? Python существует уже довольно давно. Нетрудно представить, что придется работать над каким-нибудь приложением Python 2.0, я имею в виду, что некоторым людям все еще приходится работать с VB6. - person kibibu; 14.05.2010

В Python2.7 + вы можете написать это так

>>> t = (5,6,7)
>>> d = {x:i for i,x in enumerate(t)}
>>> print d
{5: 0, 6: 1, 7: 2}
person John La Rooy    schedule 14.05.2010

Все ли ваши элементы уникальны (т.е. ваш список никогда не будет 5,6,7,7)? Решение dict будет работать только в том случае, если все ваши элементы уникальны.

Сохраняя индекс, вы, по сути, дублируете информацию, поскольку можете просто запросить текущий индекс элемента в списке. Дублирование информации обычно не лучшая идея, потому что это позволяет одному набору данных не синхронизироваться с другим.

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

Почему вы пытаетесь сохранить значение индекса, если можно просто получить индекс из списка?

person Community    schedule 14.05.2010
comment
все элементы списка уникальны. Я сохраняю индекс для быстрого поиска в другой структуре данных. - person AnalyticsBuilder; 14.05.2010
comment
Это звучит как бесполезный уровень косвенного обращения, если все элементы уникальны, проверьте членство с помощью in и индексируйте с помощью index(). Я предполагаю, что вы думаете, что словарь с хэш-картой даст вам более быстрый поиск, чем index(). В Python преждевременная оптимизация действительно является злом, потому что ваша интуиция относительно ускорения часто ошибочна, пока на самом деле не рассчитано время. Заставьте это работать, а затем выясните, где вы медлите, дополнительная сложность того не стоит. - person msw; 14.05.2010
comment
@Dragan, вы меняете свой список или он остается статичным? - person Hamish Grubijan; 14.05.2010
comment
@msw правильная проблема, в целом я согласен с вами, в этом случае я думаю, что это того стоит python -mtimeit -st = range (int (1e2)) trueVal = (55 in t) # 3.11 мкс на цикл python -mtimeit - st = диапазон (int (1e2)); d = dict (zip (t, range (len (t)))) trueVal = (55 in d) # 0,138 мксек на цикл python -mtimeit -st = range (int (1e2)) idxVal = t.index (55) # 3.46 usec на цикл python -mtimeit -st = range (int (1e2)); d = dict (zip (t, range (len (t)))) indexVal = d [55] # 0,136 мксек на цикл - person AnalyticsBuilder; 14.05.2010
comment
@dragan: утверждение подтверждено, даже когда я сильно отклонил ваш тест от словарей, используя 5 в качестве цели, dicts по-прежнему вырывал двери из списков в 3-7 раз быстрее. Я думал, что попадания / промахи кеша могли вызвать путаницу, это не похоже на мою систему, даже со списком размером 1e7, который вынудил его поменять местами. (Python 2.6.5, Linux 2.6) - person msw; 15.05.2010

Как все уже писали, в Python 2.6 наиболее питоничными я бы считал:

>>> dict((x, i) for i, x in enumerate(t))
{5: 0, 6: 1, 7: 2}

Тем не менее, в момент функционального безумия я бы написал:

>>> dict(map(reversed, enumerate(t)))
{5: 0, 6: 1, 7: 2}
person krawyoti    schedule 14.05.2010

Мне больше всего нравится dict (zip (t, range (len (t)))).

person Arafangion    schedule 14.05.2010