Compruebe si algo está (no) en una lista en Python


314

Tengo una lista de tuplas en Python , y tengo un condicional donde quiero tomar la rama SOLAMENTE si la tupla no está en la lista (si está en la lista, entonces no quiero tomar la rama if)

if curr_x -1 > 0 and (curr_x-1 , curr_y) not in myList: 

    # Do Something

Sin embargo, esto realmente no funciona para mí. ¿Qué he hecho mal?


1
Tenga en cuenta que 3 -1 > 0 and (4-1 , 5) not in []Truepor lo tanto, el error no es uno de precedencia de los operadores.
Dan D.

66
¿Qué quieres decir con "realmente no funciona para mí"? ¿Qué esperas que pase? ¿Qué pasa realmente? ¿Qué contenido exacto de la lista desencadena el problema?
Karl Knechtel

Por qué no intentarlo myList.count((curr_x, curr_y)), si (curr_x, curr_y)no está myList, el resultado será0
LittleLittleQ


2
¿Cómo esta pregunta "mi código realmente no funciona para mí" obtuvo 297 votos a favor? Por favor, danos un ejemplo reproducible mínimo .
Gerrit

Respuestas:


503

El error probablemente esté en otra parte de su código, porque debería funcionar bien:

>>> 3 not in [2, 3, 4]
False
>>> 3 not in [4, 5, 6]
True

O con tuplas:

>>> (2, 3) not in [(2, 3), (5, 6), (9, 1)]
False
>>> (2, 3) not in [(2, 7), (7, 3), "hi"]
True

11
@Zack: si no supieras sobre esto, podrías simplemente hacerloif not ELEMENT in COLLECTION:
ninjagecko

@ninjagecko: dependiendo del tipo de contenedor que podría ser menos eficiente, o incluso incorrecto. Ver por ejemplo filtros de floración .
orlp

14
@nightcracker Eso no tiene sentido ya que A not in Bse reduce a hacer lo not B.__contains__(A)que es lo mismo que lo que not A in Bse reduce a lo que es not B.__contains__(A).
Dan D.

1
Oh wow, podría haber jurado que Python tenía algo así __notcontains__. Lo siento, entonces lo que dije es una mierda.
orlp

2
@ std''OrgnlDave La única forma en que podría suceder es si nottuviera mayor prioridad que inla que no tiene. Considere el resultado de lo ast.dump(ast.parse("not A in B").body[0])que resulta en "Expr(value=UnaryOp(op=Not(), operand=Compare(left=Name(id='A', ctx=Load()), ops=[In()], comparators=[Name(id='B', ctx=Load())])))"Si se notagrupa estrechamente a A, uno hubiera esperado que el resultado fuera "Expr(value=Compare(left=UnaryOp(op=Not(), operand=Name(id='A', ctx=Load())), ops=[In()], comparators=[Name(id='B', ctx=Load())]))"cuál es el análisis "(not A) in B".
Dan D.

20

¿Cómo verifico si algo está (no) en una lista en Python?

La solución más barata y más legible es usar el inoperador (o en su caso específico not in). Como se menciona en la documentación,

Los operadores iny not inprueba de membresía. x in sevalúa Truesi xes miembro de s, y de lo Falsecontrario. x not in sdevuelve la negación de x in s.

Adicionalmente,

El operador not inse define para tener el valor verdadero inverso de in.

y not in xes lógicamente lo mismo que not y in x.

Aquí están 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 las tuplas, ya que las tuplas son hashable (como consecuencia del hecho de que también son inmutables):

(1, 2) in [(3, 4), (1, 2)]
#  True

Si el objeto en el RHS define un __contains__()método, inlo llamará internamente, como se señala en el último párrafo de la sección de Comparaciones de los documentos.

... iny not inson compatibles con tipos que son iterables o implementan el __contains__()método. Por ejemplo, podría (pero no debería) hacer esto:

[3, 2, 1].__contains__(1)
# True

incortocircuitos, por lo que si su elemento está al principio de la lista, inevalú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 desea hacer más que simplemente verificar si un elemento está en una lista, hay opciones:

  • list.indexse puede usar para recuperar el índice de un artículo. Si ese elemento no existe, ValueErrorse genera a.
  • list.count puede usarse si desea contar las ocurrencias.

El problema XY: ¿Has considerado sets?

Hágase estas preguntas:

  • ¿necesita verificar si un artículo está en una lista más de una vez?
  • ¿Se realiza esta comprobación dentro de un bucle o se llama repetidamente a una función?
  • ¿Los elementos que está almacenando en su lista son hashable? IOW, ¿puedes llamarlos hash?

Si respondió "sí" a estas preguntas, debería utilizar un seten su lugar. Una inprueba de membresía en lists es O (n) complejidad de tiempo. Esto significa que Python tiene que hacer un escaneo lineal de su lista, visitando cada elemento y comparándolo con el elemento de búsqueda. Si está haciendo esto repetidamente, o si las listas son grandes, esta operación generará una sobrecarga.

setLos objetos, por otro lado, calculan sus valores para la verificación de membresía de tiempo constante. La verificación también se realiza utilizando in:

1 in {1, 2, 3} 
# True

'a' not in {'a', 'b', 'c'}
# False

(1, 2) in {('a', 'c'), (1, 2)}
# True

Si tiene la mala suerte de que el elemento que está buscando / no está buscando está al final de su lista, python habrá escaneado la lista hasta el final. Esto es evidente por los tiempos a continuación:

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á almacenando y buscando sean intercambiables. IOW, tendrían que ser tipos inmutables u objetos que se implementen __hash__.


2
Los conjuntos no siempre son una opción (por ejemplo, cuando se tiene una lista de elementos mutables). Para colecciones grandes: construir el conjunto para una búsqueda es O (n) tiempo de todos modos y puede duplicar el uso de su memoria. Si aún no tiene una búsqueda, no siempre es la mejor opción para hacer / mantener una.
wim
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.