¿Cómo compruebo si algo está (o no) en una lista en Python?
La solución más barata y legible es utilizar la función in
(o en su caso específico, not in
). Como se menciona en la documentación,
Los operadores in
y not in
prueba de adhesión. x in s
e True
si x
es miembro de s
y False
de lo contrario. x not in s
r la negación de x in s
.
Además,
El operador not in
se define para tener el valor verdadero inverso de in
.
y not in x
es lógicamente lo mismo que not y in x
.
He aquí algunos ejemplos:
'a' in [1, 2, 3]
# False
'c' in ['a', 'b', 'c']
# True
'a' not in [1, 2, 3]
# True
'c' not in ['a', 'b', 'c']
# False
Esto también funciona con tuplas, ya que las tuplas son hashables (como consecuencia del hecho de que también son inmutables):
(1, 2) in [(3, 4), (1, 2)]
# True
Si el objeto del lado derecho define un __contains__()
método, in
lo llamará internamente, como se indica en el último párrafo del Comparaciones de los documentos.
... in
y not in
, son compatibles con el tipo __contains__()
método. Por ejemplo, podrías (pero no deberías) hacer esto:
[3, 2, 1].__contains__(1)
# True
in
cortocircuita, así que si tu elemento está al principio de la lista, in
evalúa más rápido:
lst = list(range(10001))
%timeit 1 in lst
%timeit 10000 in lst # Expected to take longer time.
68.9 ns ± 0.613 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
178 µs ± 5.01 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Si quieres hacer algo más que comprobar si un elemento está en una lista, hay opciones:
list.index
se puede utilizar para recuperar el índice de un elemento. Si ese elemento no existe, aparecerá un ValueError
se levanta.
list.count
se puede utilizar si desea contar las ocurrencias.
El problema XY: ¿Ha pensado en set
s?
Hágase estas preguntas:
- ¿necesitas comprobar si un elemento está en una lista más de una vez?
- ¿Esta comprobación se realiza dentro de un bucle, o de una función llamada repetidamente?
- ¿Los elementos que almacenas en tu lista son hashables? Es decir, ¿puede llamar a
hash
en ellos?
Si ha respondido "sí" a estas preguntas, debería utilizar un set
en su lugar. Un in
prueba de adhesión el list
s tiene una complejidad de tiempo O(n). Esto significa que Python tiene que hacer un barrido lineal de tu lista, visitando cada elemento y comparándolo con el elemento buscado. Si haces esto repetidamente, o si las listas son grandes, esta operación incurrirá en una sobrecarga.
set
por otro lado, hacen hash de sus valores para comprobar la pertenencia en tiempo constante. La comprobación también se realiza mediante in
:
1 in {1, 2, 3}
# True
'a' not in {'a', 'b', 'c'}
# False
(1, 2) in {('a', 'c'), (1, 2)}
# True
Si tienes la mala suerte de que el elemento que buscas/no buscas está al final de la lista, Python habrá escaneado la lista hasta el final. Esto es evidente a partir de los tiempos de abajo:
l = list(range(100001))
s = set(l)
%timeit 100000 in l
%timeit 100000 in s
2.58 ms ± 58.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
101 ns ± 9.53 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
Como recordatorio, esta es una opción adecuada siempre y cuando los elementos que estés almacenando y consultando sean hashables. Es decir, tendrían que ser tipos inmutables u objetos que implementen __hash__
.