Python: trova nell'elenco


586

Mi sono imbattuto in questo:

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

ma a volte non funziona con tutti i miei articoli, come se non fossero riconosciuti nell'elenco (quando è un elenco di stringhe).

È questo il modo più 'divinatorio' di trovare un elemento in un elenco: if x in l:?


3
Va benissimo e dovrebbe funzionare se l'oggetto è uguale a uno degli elementi all'interno myList.
Niklas B.

1
vuoi dire che era il buon modo di fare le cose? nelle mie varie prove, forse c'erano spazi bianchi e feed di riga che interferivano ... volevo solo essere sicuro che fosse il buon modo per implementare "trova nella lista" (in generale)
Stephane Rolland

Risposte:


1174

Per quanto riguarda la tua prima domanda: quel codice va benissimo e dovrebbe funzionare se è itemuguale a uno degli elementi all'interno myList. Forse provi a trovare una stringa che non corrisponde esattamente a uno degli elementi o forse stai usando un valore float che risente di inesattezze.

Per quanto riguarda la tua seconda domanda: in realtà ci sono diversi modi per "trovare" le cose negli elenchi.

Verifica se qualcosa è dentro

Questo è il caso d'uso che descrivi: Verifica se qualcosa è dentro una lista o no. Come sai, puoi usare l' inoperatore per questo:

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

Filtraggio di una raccolta

Cioè, trovare tutti gli elementi in una sequenza che soddisfano una determinata condizione. È possibile utilizzare la comprensione dell'elenco o le espressioni del generatore per questo:

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

Quest'ultimo restituirà un generatore che puoi immaginare come una sorta di elenco pigro che verrà creato non appena lo eseguirai. A proposito, il primo è esattamente equivalente a

matches = filter(fulfills_some_condition, lst)

in Python 2. Qui puoi vedere le funzioni di ordine superiore al lavoro. In Python 3, filternon restituisce un elenco, ma un oggetto simile a un generatore.

Trovare la prima occorrenza

Se vuoi solo la prima cosa che corrisponde a una condizione (ma non sai ancora cosa sia), va bene usare un ciclo for (possibilmente usando anche la elseclausola, che non è molto conosciuta). Puoi anche usare

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

che restituirà la prima partita o genererà un StopIterationse non ne viene trovato nessuno. In alternativa, puoi usare

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

Trovare la posizione di un oggetto

Per gli elenchi, esiste anche il indexmetodo che a volte può essere utile se vuoi sapere dove si trova un determinato elemento nell'elenco:

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

Tuttavia, tieni presente che se hai duplicati, .indexrestituisce sempre l'indice più basso: ......

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

Se ci sono duplicati e vuoi tutti gli indici, puoi enumerate()invece usare :

[i for i,x in enumerate([1,2,3,2]) if x==2] # => [1, 3]

10
Stephane: Lasciami riformulare: nonif x in list è la cosa che la gente si lamenta di non essere una funzione integrata. Si lamentano del fatto che non esiste un modo esplicito per trovare la prima occorrenza di qualcosa in un elenco che corrisponde a una determinata condizione. Ma come indicato nella mia risposta, può essere (ab) utilizzato per questo. next()
Niklas B.

3
@Stephane: il secondo non genera una tupla, ma un generatore (che in pratica è un elenco non ancora costruito). Se si desidera utilizzare il risultato solo una volta, è generalmente preferibile un generatore. Tuttavia, se si desidera utilizzare la raccolta creata più volte in seguito, è consigliabile creare un elenco esplicito in primo luogo. Dai un'occhiata al mio aggiornamento, ora è un po 'meglio strutturato :)
Niklas B.

26
Il tuo esempio "trovare la prima occorrenza" è d'oro. Sembra più pitonico [list comprehension...][0]dell'approccio
acjay

4
Sono sempre più in disaccordo con le capacità "funzionali" di Python. In haskell c'è una funzione find nel modulo Data.List che fa esattamente questo. Ma in Python non lo è ed è troppo piccolo per renderlo una libreria, quindi devi reimplementare la stessa logica più e più volte. Che spreco ...
user1685095

3
Sarebbe bello se ci fosse un kwarg da index()chiamare keyche funzioni come keyaccettato da max(); per esempio: index(list, key=is_prime).
Curt

189

Se si desidera trovare un elemento o Noneutilizzare l'impostazione predefinita in next, non aumenterà StopIterationse l'elemento non è stato trovato nell'elenco:

first_or_default = next((x for x in lst if ...), None)

1
nextaccetta un iteratore come primo parametro e un elenco / tupla NON è un iteratore. Quindi dovrebbe essere first_or_default = next(iter([x for x in lst if ...]), None)visualizzato docs.python.org/3/library/functions.html#next
Devy

7
@Devy: esatto, ma (x for x in lst if ...)è un generatore sulla lista lst(che è un iteratore). Se lo fai next(iter([x for x in lst if ...]), None), devi costruire l'elenco [x for x in lst if ...], che sarà un'operazione molto più costosa.
Erlend Graff,

1
C'è un'astrazione qui per definire una funzione di ricerca. Incapsula semplicemente l'espansione booleana di ifin a lambda e puoi scrivere di find(fn,list)solito invece di offuscare il codice del generatore.
semiomante

22

Mentre la risposta di Niklas B. è piuttosto completa, quando vogliamo trovare un elemento in un elenco a volte è utile ottenere il suo indice:

next((i for i, x in enumerate(lst) if [condition on x]), [default value])

11

Trovare la prima occorrenza

C'è una ricetta per questo in 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)

Ad esempio, il codice seguente trova il primo numero dispari in un elenco:

>>> first_true([2,3,4,5], None, lambda x: x%2==1)
3  

6

Un'altra alternativa: puoi verificare se un articolo è in un elenco con if item in list:, ma questo è l'ordine O (n). Se hai a che fare con grandi elenchi di elementi e tutto ciò che devi sapere è se qualcosa è un membro del tuo elenco, puoi prima convertirlo in un set e sfruttare la costante ricerca del set di tempo :

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

Non sarà la soluzione corretta in ogni caso, ma in alcuni casi questo potrebbe darti prestazioni migliori.

Nota che anche la creazione del set set(my_list)è O (n), quindi se hai bisogno di farlo solo una volta, non è più veloce farlo in questo modo. Se è necessario controllare più volte l'appartenenza, questo sarà O (1) per ogni ricerca dopo la creazione del set iniziale.


4

È possibile che si desideri utilizzare una delle due ricerche possibili mentre si lavora con l'elenco di stringhe:

  1. se l'elemento list è uguale a un elemento ('esempio' è in ['uno', 'esempio', 'due']):

    if item in your_list: some_function_on_true()

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

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

  2. se l'elemento list è come un elemento ('ex' è in ['uno,' esempio ',' due '] o' esempio_1 'è in [' uno ',' esempio ',' due ']):

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

    o

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

    quindi basta controllarli len(matches)o leggerli se necessario.


3

Definizione e utilizzo

il count() metodo restituisce il numero di elementi con il valore specificato.

Sintassi

list.count(value)

esempio:

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

x = fruits.count("cherry")

Esempio di domanda:

item = someSortOfSelection()

if myList.count(item) >= 1 :

    doMySpecialFunction(item)

2
È efficiente in un elenco molto lungo? Di 'un elenco di un milione?
3kstc,

1
Non sono sicuro !!!
josef

1

Invece di usare list.index(x)quale restituisce l'indice di x se viene trovato nell'elenco o restituisce un #ValueErrormessaggio se x non viene trovato, è possibile utilizzare list.count(x)quale restituisce il numero di occorrenze di x nell'elenco (convalida che x sia effettivamente nell'elenco) oppure restituisce 0 altrimenti (in assenza di x). La cosa interessante count()è che non rompe il tuo codice o ti richiede di lanciare un'eccezione per quando x non viene trovato


e la cosa brutta è che conta gli elementi. Non si ferma quando viene trovato l'elemento. quindi la performance è cattiva nelle grandi liste
Jean-François Fabre

1

Se hai intenzione di verificare se esiste un valore nella collezione una volta, allora usare l'operatore 'in' va bene. Tuttavia, se hai intenzione di controllare più di una volta, ti consiglio di utilizzare il modulo bisect. Tenere presente che è necessario ordinare i dati del modulo bisect. Quindi ordina i dati una volta e poi puoi usare bisect. L'uso del modulo bisect sulla mia macchina è circa 12 volte più veloce dell'uso dell'operatore "in".

Ecco un esempio di codice che utilizza la sintassi di Python 3.8 e successive:

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

Produzione:

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

Verificare che non vi siano spazi bianchi aggiuntivi / indesiderati negli elementi dell'elenco di stringhe. Questo è un motivo che può interferire spiegando che gli oggetti non sono stati trovati.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.