Поиск индекса элемента по списку, содержащему его в Python
Для списка ["foo", "bar", "baz"]
и элемента в списке "bar"
, какой самый чистый способ получить его индекс (1) в Python?
Ну, конечно, есть метод index, который возвращает индекс первого вхождения:
>>> l = ["foo", "bar", "baz"]
>>> l.index('bar')
1
Есть несколько проблем с этим методом:
- если значение отсутствует в списке, вы получите
ValueError
- если в списке более одного значения, вы получите индекс только для первого
Нет значений
Если значение может отсутствовать, вам нужно поймать ValueError
.
Вы можете сделать это с помощью многоразового определения, такого как:
def index(a_list, value):
try:
return a_list.index(value)
except ValueError:
return None
И используйте это так:
>>> print(index(l, 'quux'))
None
>>> print(index(l, 'bar'))
1
И недостатком этого является то, что у вас, вероятно, будет проверка, если возвращаемое значение is
или is not
Нет:
result = index(a_list, value)
if result is not None:
do_something(result)
Более одного значения в списке
Если бы вы могли иметь больше случаев, вы не получите полную информацию с помощью list.index
:
>>> l.append('bar')
>>> l
['foo', 'bar', 'baz', 'bar']
>>> l.index('bar') # nothing at index 3?
1
Вы могли бы перечислить в список, содержащий индексы:
>>> [index for index, v in enumerate(l) if v == 'bar']
[1, 3]
>>> [index for index, v in enumerate(l) if v == 'boink']
[]
Если у вас нет вхождений, вы можете проверить это с помощью логической проверки результата или просто ничего не делать, если вы просматриваете результаты:
indexes = [index for index, v in enumerate(l) if v == 'boink']
for index in indexes:
do_something(index)
Лучшее копирование данных с пандами
Если у вас есть панды, вы можете легко получить эту информацию с помощью объекта Series:
>>> import pandas as pd
>>> series = pd.Series(l)
>>> series
0 foo
1 bar
2 baz
3 bar
dtype: object
Проверка сравнения вернет серию логических значений:
>>> series == 'bar'
0 False
1 True
2 False
3 True
dtype: bool
Передайте эту серию логических значений в ряд с помощью индексной записи, и вы получите только подходящие члены:
>>> series[series == 'bar']
1 bar
3 bar
dtype: object
Если вам нужны только индексы, атрибут index возвращает последовательность целых чисел:
>>> series[series == 'bar'].index
Int64Index([1, 3], dtype='int64')
И если вы хотите, чтобы они были в списке или кортеже, просто передайте их конструктору:
>>> list(series[series == 'bar'].index)
[1, 3]
Да, вы могли бы использовать списочное понимание и с enumerate, но, на мой взгляд, это не так элегантно - вы выполняете тесты на равенство в Python вместо того, чтобы позволить встроенному коду, написанному на C, обрабатывать его:
>>> [i for i, value in enumerate(l) if value == 'bar']
[1, 3]
Проблема XY заключается в том, что вы пытаетесь решить проблему, а не в своей реальной проблеме.
Как вы думаете, почему вам нужен индекс, заданный элементом в списке?
Если вы уже знаете значение, почему вас волнует, где оно находится в списке?
Если значение отсутствует, то перехват ValueError
довольно многословен, и я предпочитаю избегать этого.
Обычно я в любом случае перебираю список, поэтому я обычно держу указатель на любую интересную информацию, получая индекс с перечислением.
Если вы манипулируете данными, вам, вероятно, следует использовать панд - у которых гораздо более изящные инструменты, чем чисто обходные пути Python, которые я показал.
Я не помню, чтобы я сам нуждался в list.index
. Тем не менее, я просмотрел стандартную библиотеку Python и нашел отличные варианты ее использования.
В idlelib
есть много, много применений для графического интерфейса пользователя и разбора текста.
Модуль keyword
использует его для поиска маркеров комментариев в модуле для автоматической регенерации списка ключевых слов в нем с помощью метапрограммирования.
В Lib / mailbox.py, похоже, он используется как упорядоченное отображение:
key_list[key_list.index(old)] = new
и
del key_list[key_list.index(key)]
В lib / http / cookiejar.py, похоже, используется для получения следующего месяца:
mon = MONTHS_LOWER.index(mon.lower())+1
В lib / tarfile.py аналогично distutils, чтобы получить фрагмент до элемента:
members = members[:members.index(tarinfo)]
В Lib / pickletools.py:
numtopop = before.index(markobject)
Что общего в этих применениях, похоже, то, что они, кажется, работают со списками ограниченных размеров (важно из-за O (n) времени поиска для list.index
), и они в основном используются при разборе (и случай простоя).
Хотя есть варианты использования, они довольно редки. Если вы ищете этот ответ, спросите себя, является ли то, что вы делаете, наиболее прямым использованием инструментов, предоставляемых языком для вашего варианта использования.