list.index () функція для Python, яка не видає винятку, коли нічого не знайдено


82

Python list.index(x)видає виняток, якщо елемент не існує. Чи є кращий спосіб зробити це, який не вимагає обробки винятків?


1
Залежить. Вам все одно, де це?
Ігнасіо Васкес-Абрамс

2
Найкращий спосіб зробити це залежить від того, що ви робите, якщо нічого не знайдено. Навіть якби у нас був list.find, який повернув -1, вам все одно потрібно буде протестувати, щоб перевірити, чи є, i == -1і вжити певних дій.
Реймонд Хеттінгер,

2
Реймонд - просто здається, що це повинен вирішувати мені, чи мій код може обробляти індекси None, а не примушувати виняток. Але тоді я все ще вчусь бути пітонічним ...
Ярін,


@yarin, як я уточнив у своїй відповіді, якщо вам не потрібен індекс, inключове слово - це те, що вам потрібно. Але якщо ви це зробите, я вважаю, що Раймонд має добру думку, і винятки - найкращий спосіб підійти до цього.
nealmcb

Відповіді:


96

Якщо вам все одно, де знаходиться відповідний елемент, використовуйте:

found = x in somelist

Якщо вам все одно, тоді використовуйте стиль LBYL з умовним виразом :

i = somelist.index(x) if x in somelist else None

2
Дякую Реймонду, це було найбільш стислою відповіддю (Знову змінено значення за замовчуванням з -1 на Ні, оскільки -1 є дійсним індексом списку)
Ярін,

1
Дякую Реймонде, LBYL був новим посиланням для мене.
Бурхан Халід

16
Але чи не такий підхід набагато повільніший, ніж при простому бігу index()? Зрештою, вам доведеться шукати двічі: один раз на існування і один раз на індекс. Ось чому в контейнерах C ++ немає exists()лише find().
Франс

9
Як сказав @frans, для цього потрібні дві перевірки, але є ще один спосіб, який може виконати роботу за один прохід:i = next((i for i, t in enumerate(somelist) if x == t), None)
AXO

@AXO Незважаючи на те, що це один прохід, він буде повільнішим для вбудованих типів, оскільки пошук виконується Python, а не кодом C.
ivan_pozdeev

7

застосувати власний індекс для списку?

class mylist(list):
  def index_withoutexception(self,i):
    try:
        return self.index(i)
    except:
        return -1

Отже, ви можете скористатися списком і разом із вашим index2 повернути те, що ви хочете на випадок помилки.

Ви можете використовувати його так:

  l = mylist([1,2,3,4,5]) # This is the only difference with a real list
  l.append(4) # l is a list.
  l.index_withoutexception(19) # return -1 or what you want

Остерігайтеся, оскільки це може зламати якийсь код: type(l) == listє Falseтут.
bfontaine

1
Крім того - я не впевнений, але мені здається, що якщо метою є уникнення створення винятків (що є дорогим, якщо це часто трапляється), то цього не вдається досягти. Він поверне -1, але всередині все одно буде створено виняток, який все одно буде дорогим.
logicOnAbstractions

6

TL; DR: Винятки - це ваш друг, і найкращий підхід до питання, як зазначено.
Простіше просити прощення, ніж дозволу (EAFP)

ОП пояснив у коментарі, що для випадку їх використання насправді не було важливо знати, що таке індекс. Як зазначає прийнята відповідь, використання x in somelist- найкраща відповідь, якщо вам все одно.

Але я припускаю , що , як припускає оригінальний питання, що ви дійсно хвилює те , що індекс. У такому випадку я зазначу, що всі інші рішення вимагають сканування списку двічі, що може призвести до великих покарань за продуктивність.

Більше того, як писав у коментарі поважний Реймонд Хеттінгер

Навіть якби у нас був list.find, який повернув -1, вам все одно потрібно було б перевірити, чи i == -1, і вжити певних дій.

Тож я повернусь до припущення у вихідному питанні, що виключень слід уникати. Я припускаю, що винятки - це ваш друг. Їх нічого не лякаєшся, вони неефективні, і насправді з ними потрібно бути знайомими, щоб написати хороший код.

Тому я вважаю, що найкраща відповідь - просто використовувати підхід за винятком:

try:
    i = somelist.index(x) 
except ValueError:
    # deal with it

" мати справу з цим " просто означає робити те, що вам потрібно зробити: встановити для i значення вартового, створити власний виняток, дотримуватися іншої гілки коду тощо.

Це приклад того, чому принцип Python Легше просити прощення, ніж дозвіл (EAFP) має сенс, на відміну від стилю if-then-else у Look before you jump (LBYL)


1
Такий справжній! Я думав, що маю цю проблему, і спочатку вважав, що одне з інших рішень було хорошим. Потім я ще раз подивився свій код і зрозумів, що використання спроби ... за винятком було б більш швидким і природним, оскільки, звичайно, мені все одно доведеться з цим боротися ....
nealmcb

1
Я голосно сміюся. Іноді розуміння не тривають. Я просто зробив коментар вище, не помітивши, що написав відповідь спочатку 2 роки тому. Тільки коли я спробував проголосувати власну відповідь, ТАК
підказав

Це абсолютно весело!
T_M

4

Напишіть функцію, яка робить те, що вам потрібно:

def find_in_iterable(x, iterable):
    for i, item in enumerate(iterable):
        if item == x:
            return i
    return None

Якщо вам потрібно лише знати, чи існує елемент, але не індекс, ви можете використовувати in:

x in yourlist

Дякую - але я сподівався на щось фасоване
Ярін

4
PS '-1' є дійсним індексом списку - вам потрібно повернути 'None'
Ярін,

@Yarin: Причиною того, що я вибрав -1, було узгодження з існуючими ідіомами Python, наприклад 'abc'.find('x') == -1, але Noneце теж буде працювати. Я оновлю свою відповідь.
Марк Байерс

1
Так, дивно, що .find () робить це - здається несумісним із негативними індексами Python.
Ярін,

3

Так, є. Ви можете, наприклад. зробити щось подібне до цього:

test = lambda l, e: l.index(e) if e in l else None

який працює так:

>>> a = ['a', 'b', 'c', 'g', 'c']
>>> test(a, 'b')
1
>>> test(a, 'c')
2
>>> test(a, 't')
None

Отже, в основному, test() буде повертатися індекс елемента (другий параметр) у межах заданого списку (перший параметр), якщо він не знайдений (у цьому випадку він повернеться None, але це може бути все, що вам підійде).


1

Якщо вам байдуже, де вона знаходиться в послідовності, лише її наявність, тоді скористайтесь inоператором. В іншому випадку напишіть функцію, яка реконструює обробку виключень.

def inlist(needle, haystack):
  try:
    return haystack.index(needle)
  except ...:
    return -1

Я опублікував відповідь, яка перевіряє наявність за допомогою inі, зрештою, повертає індекс, але я бачу, що ви знаєте цей метод і вирішили використовувати обробку винятків, які можуть бути видані. Моє запитання: чому ви вибрали ловити виняток замість того, щоб спочатку перевірити haystackнаявність needle? Чи є для цього якась причина?
Tadeck

@Tadeck: Через неписане правило Python, "простіше просити прощення, ніж дозволу".
Ігнасіо Васкес-Абрамс

1
Дякую :) Я щойно знайшов це у глосарії Python, де EAFP (простіше просити прощення, ніж про дозвіл) відображається як щось протилежне LBYL (Look Before You Leap) . І справді EAFP було названо загальним стилем кодування Python , тому він не такий неписаний :) Ще раз спасибі!
Tadeck

Вам насправді не потрібно ...в except ...:рядку.
swdev


0

Немає вбудованого способу робити те, що ти хочеш робити.

Ось хороший допис, який може вам допомогти: Чому у списку немає безпечного методу "отримати", як словник?


Ця публікація стосується індексації масиву з IndexError: list index out of rangeпроблемою, а не index()методом із ValueError: <value> is not in listпроблемою, описаним тут.
nealmcb

0

Мені подобається користуватися класом List Web2py , який міститься в модулі зберігання його пакунку gluon . Модуль зберігання пропонує подібні до списку (Список) та словникові (Зберігання) структури даних, які не викликають помилок, коли елемент не знайдений.

Спочатку завантажте джерело web2py , а потім скопіюйте та вставте папку пакунку gluon у сайт-пакети вашої інсталяції python.

Тепер спробуйте:

>>> from gluon.storage import List
>>> L = List(['a','b','c'])
>>> print L(2)
c
>>> print L(3) #No IndexError!
None

Зверніть увагу, він також може поводитися як звичайний список:

>>> print L[3]

Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
l[3]
IndexError: list index out of range

гарна ідея, дякую.)def __call__(self, idx, df=None): return self[idx] if 0<=idx<len(self) else df
Вінанд
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.