Лучший способ обработки list.index(возможно, не существует) в python?

У меня есть код, который выглядит примерно так:

thing_index = thing_list.index(thing)
otherfunction(thing_list, thing_index)

ok, чтобы упростить, но вы поняли эту идею. Теперь thing не может быть действительно в списке, и в этом случае я хочу передать -1 как thing_index. На других языках это то, что вы ожидаете index(), если он не сможет найти элемент. На самом деле он выбрасывает ValueError.

Я мог бы сделать это:

try:
    thing_index = thing_list.index(thing)
except ValueError:
    thing_index = -1
otherfunction(thing_list, thing_index)

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

thing_index = ( [(i for i in xrange(len(thing_list)) if thing_list[i]==thing)] or [-1] )[0]

Есть ли более чистый способ достичь того же? Предположим, что список не отсортирован.

Ответ 1

Нет ничего "грязного" в использовании предложения try-except. Это питонический путь. ValueError будет вызван только методом .index, потому что это единственный код, который у вас есть!

Чтобы ответить на комментарий:
В Python проще просить прощения, чем получить разрешение философия хорошо установлена, а нет index не будет поднимать этот тип ошибки для любых других проблем. Не то чтобы я мог думать о них.

Ответ 2

thing_index = thing_list.index(elem) if elem in thing_list else -1

Одна строка. Просто. Нет исключений.

Ответ 3

Тип dict имеет получить функцию, где, если ключ не существуют в словаре, второй аргумент get - это значение, которое он должен вернуть. Аналогично существует setdefault, который возвращает значение в dict, если ключ существует, в противном случае он устанавливает значение в соответствии с вашим параметром по умолчанию и затем возвращает ваш параметр по умолчанию.

Вы можете расширить тип list, чтобы иметь метод getindexdefault.

class SuperDuperList(list):
    def getindexdefault(self, elem, default):
        try:
            thing_index = self.index(elem)
            return thing_index
        except ValueError:
            return default

Что можно было бы использовать как:

mylist = SuperDuperList([0,1,2])
index = mylist.getindexdefault( 'asdf', -1 )

Ответ 4

Нет ничего плохого в вашем коде, который использует ValueError. Вот еще один лайнер, если вы хотите избежать исключений:

thing_index = next((i for i, x in enumerate(thing_list) if x == thing), -1)

Ответ 5

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

В отличие от Python всегда использовал исключения, чтобы указать нормальный поток программы, например, повышение ValueError, как мы обсуждаем здесь. В стиле Python нет ничего "грязного", и из этого есть еще много чего. Еще более распространенным примером является StopIteration exception, который выражается методом next() итератора, чтобы сигнализировать о том, что дополнительных значений нет.

Ответ 6

Как насчет этого:

otherfunction(thing_collection, thing)

Вместо того, чтобы выставлять что-то такое, зависящее от реализации, как индекс списка в функциональном интерфейсе, передавать коллекцию и вещь и позволять другим функциям разбираться с проблемами "тест для членства". Если другая функция записана как агностик типа коллекции, то она, вероятно, начнется с:

if thing in thing_collection:
    ... proceed with operation on thing

который будет работать, если thing_collection - это список, кортеж, набор или dict.

Это возможно яснее, чем:

if thing_index != MAGIC_VALUE_INDICATING_NOT_A_MEMBER:

который является кодом, который у вас уже есть в другой функции.

Ответ 7

Как насчет этого:

temp_inx = (L + [x]).index(x) 
inx = temp_inx if temp_inx < len(L) else -1

Ответ 8

У меня такая же проблема с методом ".index()" в списках. У меня нет проблем с тем, что он генерирует исключение, но я категорически не согласен с тем, что это не описательный ValueError. Я мог бы понять, если бы это был IndexError.

Я вижу, почему возвращение "-1" было бы проблемой, потому что это правильный индекс в Python. Но реалистично, я никогда не ожидаю, что метод ".index()" возвращает отрицательное число.

Здесь идет один лайнер (хорошо, это довольно длинная строка...), проходит через список ровно один раз и возвращает "Нет", если элемент не найден. Было бы тривиально переписать его, чтобы вернуть -1, если вы этого пожелаете.

indexOf = lambda list, thing: \
            reduce(lambda acc, (idx, elem): \
                   idx if (acc is None) and elem == thing else acc, list, None)

Как использовать:

>>> indexOf([1,2,3], 4)
>>>
>>> indexOf([1,2,3], 1)
0
>>>

Ответ 9

Если вы делаете это часто, то лучше сохранить это в виде вспомогательной функции:

def index_of(val, in_list):
    try:
        return in_list.index(val)
    except ValueError:
        return -1 

Ответ 10

Как насчет этого?

li = [1,2,3,4,5] # create list 

li = dict(zip(li,range(len(li)))) # convert List To Dict 
print( li ) # {1: 0, 2: 1, 3: 2, 4:3 , 5: 4}
li.get(20) # None 
li.get(1)  # 0 

Ответ 11

Я не знаю, почему вы думаете, что это грязно... из-за исключения? если вы хотите oneliner, вот он:

thing_index = thing_list.index(elem) if thing_list.count(elem) else -1

но я бы посоветовал не использовать его; Я считаю, что решение Росса Роджерса является лучшим, использовать объект для инкапсуляции вашего поведения desiderd, не пытайтесь подталкивать язык к его пределам за счет удобства чтения.

Ответ 12

Я бы предложил:

if thing in thing_list:
  list_index = -1
else:
  list_index = thing_list.index(thing)