Python: Найти в списке

587

Я сталкивался с этим:

item = someSortOfSelection()
if item in myList:
    doMySpecialFunction(item)

но иногда это не работает со всеми моими элементами, как будто они не были распознаны в списке (когда это список строк).

Является ли это наиболее «питоническим» способом поиска элемента в списке if x in l:?

Стефан Роллан
источник
3
Это прекрасно, и должно работать, если элемент равен одному из элементов внутри myList.
Никлас Б.
1
Вы имеете в виду, что это был хороший способ сделать что-то? в моих нескольких испытаниях, может быть, были пробелы и переводы строк: я просто хотел убедиться, что это хороший способ реализовать функцию «найти в списке» (в общем)
Стефан Роллан,

Ответы:

1174

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

Что касается вашего второго вопроса: на самом деле есть несколько возможных способов «найти» вещи в списках.

Проверка, если что-то внутри

Это пример использования, который вы описываете: Проверка, находится ли что-то внутри списка или нет. Как вы знаете, вы можете использовать inоператор для этого:

3 in [1, 2, 3] # => True

Фильтрация коллекции

То есть поиск всех элементов в последовательности, которые удовлетворяют определенному условию. Для этого вы можете использовать списки или выражения генератора:

matches = [x for x in lst if fulfills_some_condition(x)]
matches = (x for x in lst if x > 6)

Последний вернет генератор, который вы можете представить как некий ленивый список, который будет создан только после того, как вы выполните его итерацию. Кстати, первый точно соответствует

matches = filter(fulfills_some_condition, lst)

в Python 2. Здесь вы можете увидеть функции высшего порядка в работе. В Python 3 filterвозвращает не список, а объект, похожий на генератор.

Нахождение первого вхождения

Если вам нужно только первое, что соответствует условию (но вы еще не знаете, что это такое), можно использовать цикл for (возможно, также с помощью elseпредложения, которое не очень хорошо известно). Вы также можете использовать

next(x for x in lst if ...)

который вернет первое совпадение или поднимет a, StopIterationесли ничего не найдено. Кроме того, вы можете использовать

next((x for x in lst if ...), [default value])

Нахождение местоположения предмета

Для списков есть также indexметод, который иногда может быть полезен, если вы хотите знать, где находится определенный элемент в списке:

[1,2,3].index(2) # => 1
[1,2,3].index(4) # => ValueError

Однако обратите внимание, что если у вас есть дубликаты, .indexвсегда возвращается самый низкий индекс: ......

[1,2,3,2].index(2) # => 1

Если есть дубликаты и вам нужны все индексы, вы можете использовать enumerate()вместо:

[i for i,x in enumerate([1,2,3,2]) if x==2] # => [1, 3]
Никлас Б.
источник
10
Stephane: Позвольте мне перефразировать: if x in listэто не вещь , что люди жалуются , не будучи встроенной функцией. Они жалуются на то, что не существует явного способа найти первое появление чего-либо в списке, который соответствует определенному условию. Но, как указано в моем ответе, next()(ab) может быть использовано для этого.
Никлас Б.
3
@Stephane: Второй генерирует не кортеж, а генератор (в основном это еще не построенный список). Если вы хотите использовать результат только один раз, генератор обычно предпочтительнее. Однако, если вы хотите использовать созданную коллекцию несколько раз впоследствии, желательно сначала создать явный список. Посмотрите мое обновление, теперь оно немного лучше структурировано :)
Niklas B.
26
Ваш пример "найти первое вхождение" - золотой. Чувствуется более питоническим, чем [list comprehension...][0]подход
Acjay
4
Я все больше и больше отношусь к «функциональным» возможностям Python. В haskell есть функция find в модуле Data.List, которая делает именно это. Но в Python это не так и мало, чтобы сделать его библиотекой, поэтому вам придется переопределять одну и ту же логику снова и снова. Какая трата ...
user1685095
3
Было бы хорошо , если бы там был kwarg к index()называется , keyкоторая работала как и keyпринято max(); например: index(list, key=is_prime).
Курт
189

Если вы хотите найти один элемент или Noneиспользовать по умолчанию next, он не будет вызываться, StopIterationесли элемент не найден в списке:

first_or_default = next((x for x in lst if ...), None)
Януш Сконечны
источник
1
nextпринимает итератор в качестве первого параметра, а список / кортеж НЕ является итератором. Так что следует first_or_default = next(iter([x for x in lst if ...]), None)посмотреть docs.python.org/3/library/functions.html#next
Деви
7
@Devy: это верно, но (x for x in lst if ...)это генератор над списком lst(который является итератором). Если вы это сделаете next(iter([x for x in lst if ...]), None), вы должны [x for x in lst if ...]создать список , который будет гораздо более дорогой операцией.
Эрленд Графф
1
Здесь есть абстракция для определения функции поиска. Просто инкапсулируйте логическое выражение ifв лямбда- выражении, и вы можете писать find(fn,list)вместо того, чтобы запутывать код генератора.
полуомант
22

Хотя ответ от Niklas B. довольно исчерпывающий, когда мы хотим найти элемент в списке, иногда бывает полезно получить его индекс:

next((i for i, x in enumerate(lst) if [condition on x]), [default value])
Винсент Кантин
источник
11

Нахождение первого вхождения

Есть рецепт для этого в itertools:

def first_true(iterable, default=False, pred=None):
    """Returns the first true value in the iterable.

    If no true value is found, returns *default*

    If *pred* is not None, returns the first item
    for which pred(item) is true.

    """
    # first_true([a,b,c], x) --> a or b or c or x
    # first_true([a,b], x, f) --> a if f(a) else b if f(b) else x
    return next(filter(pred, iterable), default)

Например, следующий код находит первое нечетное число в списке:

>>> first_true([2,3,4,5], None, lambda x: x%2==1)
3  
Энтони Хэтчкинс
источник
6

Другой вариант: вы можете проверить, есть ли элемент в списке if item in list:, но это порядок O (n). Если вы имеете дело с большими списками элементов, и все, что вам нужно знать, это то, является ли что-то членом вашего списка, вы можете сначала преобразовать список в набор и воспользоваться преимуществом поиска набора с постоянным временем :

my_set = set(my_list)
if item in my_set:  # much faster on average than using a list
    # do something

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

Обратите внимание, что создание набора с помощью set(my_list)также является O (n), поэтому, если вам нужно сделать это только один раз, то делать это не быстрее. Если вам необходимо повторно проверять членство, тогда это будет O (1) для каждого поиска после создания этого начального набора.

Engineero
источник
4

Вы можете использовать один из двух возможных поисков при работе со списком строк:

  1. если элемент списка равен элементу («example» находится в [«one», «example», «two»]):

    if item in your_list: some_function_on_true()

    'ex' в ['one', 'ex', 'two'] => True

    'ex_1' в ['one', 'ex', 'two'] => False

  2. если элемент списка похож на элемент ('ex' находится в ['one,' example ',' two '] или' example_1 'находится в [' one ',' example ',' two ']):

    matches = [el for el in your_list if item in el]

    или

    matches = [el for el in your_list if el in item]

    затем просто проверьте len(matches)или прочитайте их, если это необходимо.

Алексей Антоненко
источник
3

Определение и использование

count()метод возвращает количество элементов с заданным значением.

Синтаксис

list.count(value)

пример:

fruits = ['apple', 'banana', 'cherry']

x = fruits.count("cherry")

Пример вопроса:

item = someSortOfSelection()

if myList.count(item) >= 1 :

    doMySpecialFunction(item)
йозеф
источник
2
Это эффективно в очень длинном списке? Скажи список из миллиона?
3kstc
1
Я не уверена !!!
йозеф
1

Вместо использования list.index(x)которого возвращает индекс x, если он найден в списке, или возвращает #ValueErrorсообщение, если x не найден, вы можете использовать list.count(x)метод, который возвращает число вхождений x в список (проверка того, что x действительно находится в списке), или его в противном случае возвращает 0 (при отсутствии x). Самое интересное в том, count()что он не нарушает ваш код и не требует от вас исключения, когда x не найден

Тейлор
источник
и плохо то, что он считает элементы. Не останавливается, когда элемент найден. так что производительность в больших списках плохая
Жан-Франсуа Фабр
1

Если вы собираетесь проверить, существует ли значение в предмете коллекционирования один раз, тогда лучше использовать оператор «in». Однако, если вы собираетесь проверять более одного раза, я рекомендую использовать модуль bisect. Имейте в виду, что при использовании модуля bisect данные должны быть отсортированы. Таким образом, вы сортируете данные один раз, а затем можете использовать bisect. Использование модуля bisect на моей машине примерно в 12 раз быстрее, чем использование оператора «in».

Вот пример кода с использованием синтаксиса Python 3.8 и выше:

import bisect
from timeit import timeit

def bisect_search(container, value):
    return (
      (index := bisect.bisect_left(container, value)) < len(container) 
      and container[index] == value
    )

data = list(range(1000))
# value to search
true_value = 666
false_value = 66666

# times to test
ttt = 1000

print(f"{bisect_search(data, true_value)=} {bisect_search(data, false_value)=}")

t1 = timeit(lambda: true_value in data, number=ttt)
t2 = timeit(lambda: bisect_search(data, true_value), number=ttt)

print("Performance:", f"{t1=:.4f}, {t2=:.4f}, diffs {t1/t2=:.2f}")

Вывод:

bisect_search(data, true_value)=True bisect_search(data, false_value)=False
Performance: t1=0.0220, t2=0.0019, diffs t1/t2=11.71
Влад Безден
источник
0

Убедитесь, что в элементах списка строк нет дополнительных / нежелательных пробелов. Это причина, которая может мешать объяснению предметов не может быть найдено.

Стефан Роллан
источник