Python: buscar en la lista

587

Me he encontrado con esto:

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

pero a veces no funciona con todos mis elementos, como si no fueran reconocidos en la lista (cuando es una lista de cadenas).

¿Es esta la forma más 'pitónica' de encontrar un elemento en una lista if x in l::?

Stephane Rolland
fuente
3
Eso está perfectamente bien y debería funcionar si el elemento es igual a uno de los elementos dentro myList.
Niklas B.
1
¿Quieres decir que era la buena manera de hacer las cosas? en mis varias pruebas, tal vez hubo espacios en blanco, y los avances de línea interfirieron ... Solo quería asegurarme de que es la buena manera de implementar "buscar en la lista" (en general)
Stephane Rolland

Respuestas:

1174

En cuanto a su primera pregunta: ese código está perfectamente bien y debería funcionar si itemes igual a uno de los elementos dentro myList. Tal vez intente encontrar una cadena que no coincida exactamente con uno de los elementos o tal vez esté utilizando un valor flotante que sufre de inexactitud.

En cuanto a su segunda pregunta: en realidad hay varias formas posibles de "encontrar" cosas en las listas.

Comprobando si hay algo adentro

Este es el caso de uso que describe: Verificar si algo está dentro de una lista o no. Como sabes, puedes usar el inoperador para eso:

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

Filtrando una colección

Es decir, encontrar todos los elementos en una secuencia que cumplan una determinada condición. Puede usar la comprensión de listas o expresiones generadoras para eso:

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

Este último devolverá un generador que puede imaginar como una especie de lista perezosa que solo se creará tan pronto como lo repita. Por cierto, el primero es exactamente equivalente a

matches = filter(fulfills_some_condition, lst)

en Python 2. Aquí puede ver las funciones de orden superior en funcionamiento. En Python 3, filterno devuelve una lista, sino un objeto tipo generador.

Encontrar la primera ocurrencia

Si solo quiere lo primero que coincide con una condición (pero aún no sabe qué es), está bien usar un bucle for (posiblemente también usando la elsecláusula, que no es muy conocida). También puedes usar

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

que devolverá el primer partido o aumentará a StopIterationsi no se encuentra ninguno. Alternativamente, puedes usar

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

Encontrar la ubicación de un artículo

Para las listas, también existe el indexmétodo que a veces puede ser útil si desea saber dónde está un determinado elemento en la lista:

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

Sin embargo, tenga en cuenta que si tiene duplicados, .indexsiempre devuelve el índice más bajo: ......

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

Si hay duplicados y desea todos los índices, puede usar enumerate()en su lugar:

[i for i,x in enumerate([1,2,3,2]) if x==2] # => [1, 3]
Niklas B.
fuente
10
Stephane: Permítanme reformularlo: noif x in list es de lo que la gente se queja porque no es una función incorporada. Se quejan del hecho de que no hay una forma explícita de encontrar la primera aparición de algo en una lista que coincida con una determinada condición. Pero como se indica en mi respuesta, puede (ab) usarse para eso. next()
Niklas B.
3
@Stephane: el segundo no genera una tupla, sino un generador (que es una lista aún no construida, básicamente). Si desea utilizar el resultado solo una vez, generalmente se prefiere un generador. Sin embargo, si desea usar la colección creada varias veces después, es recomendable crear una lista explícita en primer lugar. Echa un vistazo a mi actualización, ahora está un poco mejor estructurada :)
Niklas B.
26
Su ejemplo de "encontrar la primera aparición" es dorado. Se siente más pitónico que el [list comprehension...][0]enfoque
acjay
44
Estoy cada vez más decepcionado con las capacidades 'funcionales' de Python. En haskell hay una función de búsqueda en el módulo Data.List que hace exactamente eso. Pero en Python no lo es y es demasiado pequeño para convertirlo en una biblioteca, por lo que debe volver a implementar la misma lógica una y otra vez. Qué desperdicio ...
user1685095
3
Sería bueno si hubiera un kwarg index()llamado keyque funcionara como el keyaceptado por max(); por ejemplo: index(list, key=is_prime).
Curt
189

Si desea encontrar un elemento o Noneusar el valor predeterminado next, no aparecerá StopIterationsi el elemento no se encuentra en la lista:

first_or_default = next((x for x in lst if ...), None)
Janusz Skonieczny
fuente
1
nexttoma un iterador como primer parámetro y una lista / tupla NO es un iterador. Por lo tanto, debería first_or_default = next(iter([x for x in lst if ...]), None)ver docs.python.org/3/library/functions.html#next
Devy
77
@Devy: es cierto, pero (x for x in lst if ...)es un generador sobre la lista lst(que es un iterador). Si lo hace next(iter([x for x in lst if ...]), None), debe construir la lista [x for x in lst if ...], que será una operación mucho más costosa.
Erlend Graff
1
Aquí hay una abstracción para definir una función de búsqueda. Simplemente encapsule la expresión booleana del ifen una lambda y puede escribir find(fn,list)generalmente en lugar de ofuscar el código del generador.
semiomante
22

Si bien la respuesta de Niklas B. es bastante completa, cuando queremos encontrar un elemento en una lista, a veces es útil obtener su índice:

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

Encontrar la primera ocurrencia

Hay una receta para eso en 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)

Por ejemplo, el siguiente código encuentra el primer número impar en una lista:

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

Otra alternativa: puede verificar si un elemento está en una lista if item in list:, pero este es el orden O (n). Si se trata de grandes listas de elementos y todo lo que necesita saber es si algo es miembro de su lista, puede convertir la lista a un conjunto primero y aprovechar la búsqueda de conjuntos de tiempo constante :

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

No será la solución correcta en todos los casos, pero en algunos casos esto podría brindarle un mejor rendimiento.

Tenga en cuenta que crear el conjunto set(my_list)también es O (n), por lo que si solo necesita hacer esto una vez, no será más rápido hacerlo de esta manera. Sin embargo, si necesita verificar la membresía repetidamente, entonces será O (1) para cada búsqueda después de la creación del conjunto inicial.

Engineero
fuente
4

Es posible que desee utilizar una de las dos búsquedas posibles mientras trabaja con la lista de cadenas:

  1. si el elemento de la lista es igual a un elemento ('ejemplo' está en ['uno', 'ejemplo', 'dos']):

    if item in your_list: some_function_on_true()

    'ex' en ['uno', 'ex', 'dos'] => Verdadero

    'ex_1' en ['uno', 'ex', 'dos'] => Falso

  2. si el elemento de la lista es como un elemento ('ex' está en ['uno,' ejemplo ',' dos '] o' ejemplo_1 'está en [' uno ',' ejemplo ',' dos ']):

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

    o

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

    luego simplemente revíselos len(matches)o léalos si es necesario.

Alexey Antonenko
fuente
3

Definición y uso

El count()método devuelve el número de elementos con el valor especificado.

Sintaxis

list.count(value)

ejemplo:

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

x = fruits.count("cherry")

Ejemplo de pregunta:

item = someSortOfSelection()

if myList.count(item) >= 1 :

    doMySpecialFunction(item)
josef
fuente
2
¿Es esto eficiente en una lista muy larga? Decir lista de un millón?
3kstc
1
No estoy seguro !!!
josef
1

En lugar de usar el list.index(x)que devuelve el índice de x si se encuentra en la lista o devuelve un #ValueErrormensaje si no se encuentra x, podría usar el list.count(x)que devuelve el número de ocurrencias de x en la lista (validación de que x está realmente en la lista) o devuelve 0 de lo contrario (en ausencia de x). Lo bueno count()es que no rompe su código ni requiere que arroje una excepción para cuando no se encuentra x

Taylor
fuente
y lo malo es que cuenta elementos. No se detiene cuando se encuentra el elemento. así que el rendimiento es malo en las grandes listas
Jean-François Fabre
1

Si va a verificar si el valor existe en el objeto coleccionable una vez, entonces usar el operador 'in' está bien. Sin embargo, si va a verificar más de una vez, le recomiendo usar el módulo bisect. Tenga en cuenta que el uso de datos de módulo bisecado debe ordenarse. Entonces ordena los datos una vez y luego puede usar bisect. Usar el módulo bisect en mi máquina es aproximadamente 12 veces más rápido que usar el operador 'in'.

Aquí hay un ejemplo de código que usa Python 3.8 y la sintaxis anterior:

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

Salida:

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
fuente
0

Verifique que no haya espacios en blanco adicionales / no deseados en los elementos de la lista de cadenas. Esa es una razón que puede interferir explicando que no se pueden encontrar los elementos.

Stephane Rolland
fuente