Python: Znajdź na liście

583

Natknąłem się na to:

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

ale czasami to nie działa ze wszystkimi moimi elementami, tak jakby nie zostały rozpoznane na liście (gdy jest to lista ciągów znaków).

Czy jest to najbardziej „pytonowy” sposób znajdowania pozycji na liście if x in l::?

Stephane Rolland
źródło
3
Jest to całkowicie w porządku i powinno działać, jeśli element równa się jednemu z elementów w środku myList.
Niklas B.
1
masz na myśli, że to był dobry sposób na robienie rzeczy? w moich kilku próbach być może pojawiły się białe spacje i interferencje kanałów ... chciałem się tylko upewnić, że jest to dobry sposób na wdrożenie „znajdź na liście” (ogólnie)
Stephane Rolland

Odpowiedzi:

1173

Jeśli chodzi o twoje pierwsze pytanie: ten kod jest całkowicie w porządku i powinien działać, jeśli itemrówna się jednemu z elementów w środku myList. Być może próbujesz znaleźć ciąg, który nie pasuje dokładnie do jednego z elementów, lub może używasz wartości zmiennoprzecinkowej, która cierpi z powodu niedokładności.

Jeśli chodzi o twoje drugie pytanie: istnieje kilka możliwych sposobów „znalezienia” rzeczy na listach.

Sprawdzanie, czy coś jest w środku

Oto opisany przypadek użycia: Sprawdzanie, czy coś znajduje się na liście, czy nie. Jak wiesz, możesz do tego użyć inoperatora:

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

Filtrowanie kolekcji

Znalezienie wszystkich elementów w sekwencji, które spełniają określony warunek. W tym celu możesz użyć rozumienia listy lub wyrażeń generatora:

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

Ten ostatni zwróci generator, który możesz sobie wyobrazić jako rodzaj leniwej listy, która zostanie zbudowana, gdy tylko ją przejdziesz. Nawiasem mówiąc, pierwszy jest dokładnie równoważny z

matches = filter(fulfills_some_condition, lst)

w Python 2. Tutaj możesz zobaczyć funkcje wyższego rzędu w pracy. W Pythonie 3 filternie zwraca listy, ale obiekt podobny do generatora.

Znalezienie pierwszego wystąpienia

Jeśli chcesz tylko pierwszą rzecz, która pasuje do warunku (ale jeszcze nie wiesz, co to jest), dobrze jest użyć pętli for (być może również użyć elseklauzuli, która nie jest tak naprawdę dobrze znana). Możesz także użyć

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

który zwróci pierwszy mecz lub podbije, StopIterationjeśli nie zostanie znaleziony. Alternatywnie możesz użyć

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

Znalezienie położenia przedmiotu

W przypadku list istnieje również indexmetoda, która może być czasem przydatna, jeśli chcesz wiedzieć, gdzie jest określony element na liście:

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

Pamiętaj jednak, że jeśli masz duplikaty, .indexzawsze zwraca najniższy indeks: ......

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

Jeśli są duplikaty i chcesz mieć wszystkie indeksy, możesz enumerate()zamiast tego użyć :

[i for i,x in enumerate([1,2,3,2]) if x==2] # => [1, 3]
Niklas B.
źródło
10
Stephane: Pozwól mi przeformułować go: if x in listto nie rzecz, że ludzie skarżą się, nie będąc wbudowana funkcja. Skarżą się na fakt, że nie ma jednoznacznego sposobu znalezienia pierwszego wystąpienia czegoś na liście, które pasuje do określonego warunku. Ale jak stwierdzono w mojej odpowiedzi, next()można (ab) do tego wykorzystać.
Niklas B.
3
@Stephane: Drugi nie generuje krotki, ale generator (który w zasadzie nie jest jeszcze zbudowaną listą). Jeśli chcesz użyć wyniku tylko raz, zwykle preferowany jest generator. Jeśli jednak chcesz użyć utworzonej kolekcji kilka razy później, wskazane jest utworzenie wyraźnej listy. Spójrz na moją aktualizację, teraz jest ona trochę lepiej zorganizowana :)
Niklas B.
26
Twój przykład „znalezienia pierwszego wystąpienia” jest złoty. Czuje się bardziej pytoniczny niż [list comprehension...][0]podejście
acjay
4
Jestem coraz bardziej zawiedziony możliwościami „funkcjonalnymi” Pythona. W haskell znajduje się funkcja znajdująca się w module Data.List, która właśnie to robi. Ale w Pythonie tak nie jest i jest zbyt małe, aby uczynić z niego bibliotekę, więc musisz zaimplementować tę samą logikę w kółko. Co za strata ...
użytkownik1685095,
3
Byłoby miło, gdyby do wywołania był kwarg, który działałby index()tak key, jak keyzaakceptował max(); na przykład: index(list, key=is_prime).
Curt
189

Jeśli chcesz znaleźć jeden element lub Noneużyć domyślnego w next, nie podniesie się, StopIterationjeśli element nie został znaleziony na liście:

first_or_default = next((x for x in lst if ...), None)
Janusz Skonieczny
źródło
1
nextprzyjmuje iterator jako pierwszy parametr, a lista / krotka NIE jest iteratorem. Tak powinno być first_or_default = next(iter([x for x in lst if ...]), None)zobacz docs.python.org/3/library/functions.html#next
Devy
7
@Devy: zgadza się, ale (x for x in lst if ...)jest generatorem nad listą lst(który jest iteratorem). Jeśli to zrobisz next(iter([x for x in lst if ...]), None), musisz zbudować listę [x for x in lst if ...], co będzie znacznie droższą operacją.
Erlend Graff,
1
Znajduje się tutaj abstrakcja do zdefiniowania funkcji wyszukiwania. Po prostu obuduj boolowską ekspozycję ifw lambda i możesz pisać find(fn,list)zwykle zamiast zaciemniać kod generatora.
semiomant
22

Chociaż odpowiedź od Niklasa B. jest dość wyczerpująca, kiedy chcemy znaleźć pozycję na liście, czasem przydatne jest uzyskanie jej indeksu:

next((i for i, x in enumerate(lst) if [condition on x]), [default value])
Vincent Cantin
źródło
11

Znalezienie pierwszego wystąpienia

Jest na to przepis w 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)

Na przykład poniższy kod znajduje pierwszą liczbę nieparzystą na liście:

>>> first_true([2,3,4,5], None, lambda x: x%2==1)
3  
Antony Hatchkins
źródło
6

Inna alternatywa: możesz sprawdzić, czy pozycja znajduje się na liście if item in list:, ale jest to kolejność O (n). Jeśli masz do czynienia z dużymi listami przedmiotów i wszystko, co musisz wiedzieć, to czy coś jest członkiem listy, możesz najpierw przekonwertować listę na zestaw i skorzystać z ciągłego wyszukiwania zestawu czasu :

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

Nie zawsze będzie to właściwe rozwiązanie, ale w niektórych przypadkach może to dać lepszą wydajność.

Zauważ, że utworzenie zestawu za pomocą set(my_list)to także O (n), więc jeśli musisz to zrobić tylko raz, nie jest to szybsze. Jeśli jednak trzeba wielokrotnie sprawdzać członkostwo, będzie to O (1) dla każdego wyszukiwania po utworzeniu zestawu początkowego.

Engineero
źródło
4

Podczas pracy z listą ciągów możesz użyć jednego z dwóch możliwych wyszukiwań:

  1. jeśli element listy jest równy elementowi („przykład” znajduje się w [„jeden”, „przykład”, „dwa”]):

    if item in your_list: some_function_on_true()

    „ex” w [„one”, „ex”, „two”] => True

    „ex_1” w [„one”, „ex”, „two”] => False

  2. jeśli element listy jest jak element („ex” znajduje się w [„one,” example ”,„ two ”] lub„ example_1 ”znajduje się w [„ one ”,„ example ”,„ two ”]:

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

    lub

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

    w len(matches)razie potrzeby po prostu je sprawdź lub przeczytaj.

Aleksiej Antonienko
źródło
3

Definicja i zastosowanie

count()sposób powraca liczbę elementów o określonej wartości.

Składnia

list.count(value)

przykład:

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

x = fruits.count("cherry")

Przykład pytania:

item = someSortOfSelection()

if myList.count(item) >= 1 :

    doMySpecialFunction(item)
Josef
źródło
2
Czy jest to skuteczne na bardzo długiej liście? Powiedz listę milionów?
3kstc,
1
Nie jestem pewien !!!
josef
1

Zamiast używać, list.index(x)który zwraca indeks x, jeśli zostanie znaleziony na liście lub zwraca #ValueErrorkomunikat, jeśli x nie zostanie znaleziony, możesz użyć, list.count(x)który zwraca liczbę wystąpień x na liście (sprawdzanie, czy x rzeczywiście znajduje się na liście) lub zwraca 0 w przeciwnym razie (przy braku x). Fajną rzeczą count()jest to, że nie psuje kodu ani nie wymaga wyjątku, gdy x nie zostanie znalezione

Taylor
źródło
a złą rzeczą jest to, że liczy elementy. Nie zatrzymuje się po znalezieniu elementu. więc wyniki są złe na dużych listach
Jean-François Fabre
1

Jeśli masz zamiar sprawdzić, czy wartość istnieje w kolekcji, to użycie operatora „w” jest w porządku. Jeśli jednak masz zamiar sprawdzić więcej niż jeden raz, zalecamy użycie modułu bisect. Należy pamiętać, że przy użyciu danych modułu dwusiecznego należy posortować. Więc sortujesz dane raz, a potem możesz użyć dwusiecznej. Korzystanie z modułu dwusiecznego na mojej maszynie jest około 12 razy szybsze niż użycie operatora „w”.

Oto przykład kodu używającego składni Python 3.8 i nowszej:

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}")

Wynik:

bisect_search(data, true_value)=True bisect_search(data, false_value)=False
Performance: t1=0.0220, t2=0.0019, diffs t1/t2=11.71
Vlad Bezden
źródło
0

Sprawdź, czy w elementach listy ciągów nie ma dodatkowych / niechcianych białych znaków. To jest powód, który może przeszkadzać, wyjaśniając, że nie można znaleźć przedmiotów.

Stephane Rolland
źródło