Eliminar elemento de la lista de Python

Tengo dos lista,

l1 = [1,2,3,4,5,6] l2 = [3,2] 

Lo que quiero es eliminar el elemento de la lista l1 que está en l2, para eso he hecho algo como esto,

 for x in l1: if x in l2: l1.remove(x) 

da salida como

 [1, 3, 4, 5, 6] 

pero la salida debería ser como

 [1, 4, 5, 6] 

¿Alguien puede arrojar luz sobre esto?

Esto se explica fácilmente de esta manera.

considera la primera matriz que tienes:

 | 1 | 2 | 3 | 4 | 5 | 6 | 

Ahora empiezas a iterar

 | 1 | 2 | 3 | 4 | 5 | 6 | ^ 

No pasa nada, incrementos de iterador.

 | 1 | 2 | 3 | 4 | 5 | 6 | ^ 

2 se quita

 | 1 | 3 | 4 | 5 | 6 | ^ 

incrementos de iterador

 | 1 | 3 | 4 | 5 | 6 | ^ 

Y voila, 3 sigue ahí.

La solución es iterar sobre una copia del vector, por ejemplo,

 for x in l1[:]: <- slice on entire array if x in l2: l1.remove(x) 

o iterar a la inversa:

 for x in reversed(l1): if x in l2: l1.remove(x) 

Que actúa así:

 | 1 | 2 | 3 | 4 | 5 | 6 | ^ | 1 | 2 | 3 | 4 | 5 | 6 | ^ | 1 | 2 | 4 | 5 | 6 | ^ | 1 | 2 | 4 | 5 | 6 | ^ | 1 | 4 | 5 | 6 | ^ | 1 | 4 | 5 | 6 | ^ 

¿Por qué no hacerlo un poco más simple? No es necesario iterar realmente sobre l1 si solo queremos eliminar los elementos presentes en l2 :

 for item in l2: while item in l1: l1.remove(item) 

Esto le da exactamente la salida deseada …

Además, como señalan los comentaristas, si existe la posibilidad de que podamos tener duplicados:

 l1 = filter(lambda x: x not in l2, l1) 

.. o muchas otras variaciones usando listas de comprensión.

Quieres que el bucle exterior lea:

 for x in l1[:]: ... 

No puede cambiar una lista mientras está iterando sobre ella y esperar resultados razonables. El truco anterior hace una copia de l1 e itera sobre la copia en su lugar.

Tenga en cuenta que si el orden no importa en la lista de salida, y sus elementos son únicos y hashable, podría usar un conjunto:

 set(l1).difference(l2) 

lo que le dará un conjunto como resultado, pero puede construir una lista a partir de él fácilmente:

 l1 = list(set(l1).difference(l2)) 

Como han dicho otros, no puedes editar una lista mientras lo recorres. Una buena opción aquí es usar una lista de comprensión para crear una nueva lista.

 removals = set(l2) l1 = [item for item in l1 if item not in removals] 

Hacemos un conjunto como un control de membresía en un conjunto es significativamente más rápido que en una lista.

Si el orden y la pérdida de duplicados en l1 no importan:

 list(set(l1) - set(l2)) 

La última lista () solo es necesaria si necesita el resultado como una lista. También puedes usar el conjunto resultante, también es iterable. Si lo necesita, puede llamar a l.sort () en la lista resultante.

Edit: Eliminé mi respuesta original porque, aunque dio resultados correctos, lo hizo por razones no intuitivas, y tampoco fue muy rápido … así que acabo de dejar los horarios:

 import timeit setup = """l1 = list(range(20)) + list(range(20)) l2 = [2, 3]""" stmts = { "mgilson": """for x in l1[:]: if x in l2: l1.remove(x)""", "petr": """for item in l2: while item in l1: l1.remove(item)""", "Lattyware": """removals = set(l2) l1 = [item for item in l1 if item not in removals]""", "millimoose": """for x in l2: try: while True: l1.remove(x) except ValueError: pass""", "Latty_mgilson": """removals = set(l2) l1[:] = (item for item in l1 if item not in removals)""", "mgilson_set": """l1 = list(set(l1).difference(l2))""" } for idea in stmts: print("{0}: {1}".format(idea, timeit.timeit(setup=setup, stmt=stmts[idea]))) 

Resultados (Python 3.3.0 64bit, Win7):

 mgilson_set: 2.5841989922197333 mgilson: 3.7747968857414813 petr: 1.9669433777815701 Latty_mgilson: 7.262900152285258 millimoose: 3.1890831105541793 Lattyware: 4.573971325181478 

Estás modificando la lista l1 mientras estás iterando sobre ella, esto causará un comportamiento extraño. (Los 3 se omitirán durante la iteración).

O bien iterar sobre una copia, o cambiar su algoritmo para iterar sobre l2 lugar:

 for x in l2: try: while True: l1.remove(x) except ValueError: pass 

(Esto debería funcionar mejor que probar if x in l1 explícitamente.) No, esto funciona terriblemente a medida que l1 crece en tamaño.

FWIW Tengo resultados significativamente diferentes a los de @Tim Pietzcker usando lo que creo que es un conjunto de datos de entrada más realista y utilizando un enfoque un poco más riguroso (pero por lo demás el mismo) para sincronizar las respuestas de diferentes personas.

Los nombres y los fragmentos de código son los mismos que los de Tim, excepto que agregué una variación del llamado Lattyware llamado Lattyware_rev que determina qué elementos mantener en lugar de rechazar: resultó ser más lento que el anterior. Tenga en cuenta que los dos más rápidos no conservan el orden de l1 .

Aquí está el último código de tiempo:

 import timeit setup = """ import random random.seed(42) # initialize to constant to get same test values l1 = [random.randrange(100) for _ in xrange(100)] l2 = [random.randrange(100) for _ in xrange(10)] """ stmts = { "Minion91": """ for x in reversed(l1): if x in l2: l1.remove(x) """, "mgilson": """ for x in l1[:]: # correction if x in l2: l1.remove(x) """, "mgilson_set": """ l1 = list(set(l1).difference(l2)) """, "Lattyware": """ removals = set(l2) l1 = [item for item in l1 if item not in removals] """, "Lattyware_rev": """ keep = set(l1).difference(l2) l1 = [item for item in l1 if item in keep] """, "Latty_mgilson": """ removals = set(l2) l1[:] = (item for item in l1 if item not in removals)""", "petr": """ for item in l2: while item in l1: l1.remove(item) """, "petr (handles dups)": """ l1 = filter(lambda x: x not in l2, l1) """, "millimoose": """ for x in l2: try: while True: l1.remove(x) except ValueError: pass """, "K.-Michael Aye": """ l1 = list(set(l1) - set(l2)) """, } N = 10000 R = 3 timings = [(idea, min(timeit.repeat(stmts[idea], setup=setup, repeat=R, number=N)), ) for idea in stmts] longest = max(len(t[0]) for t in timings) # length of longest name exec(setup) # get an l1 & l2 just for heading length measurements print('fastest to slowest timings of ideas:\n' +\ ' ({:,d} timeit calls, best of {:d} executions)\n'.format(N, R)+\ ' len(l1): {:,d}, len(l2): {:,d})\n'.format(len(l1), len(l2))) for i in sorted(timings, key=lambda x: x[1]): # sort by speed (fastest first) print "{:>{width}}: {}".format(*i, width=longest) 

Salida:

 fastest to slowest timings of ideas: (10,000 timeit calls, best of 3 executions) len(l1): 100, len(l2): 10) mgilson_set: 0.143126456832 K.-Michael Aye: 0.213544010551 Lattyware: 0.23666971551 Lattyware_rev: 0.466918513924 Latty_mgilson: 0.547516608553 petr: 0.552547776807 mgilson: 0.614238139366 Minion91: 0.728920176815 millimoose: 0.883061820848 petr (handles dups): 0.984093136969 

Por supuesto, avíseme si hay algo radicalmente incorrecto que explique los resultados radicalmente diferentes.

 l1 = [1, 2, 3, 4, 5, 6] l2 = [3, 2] [l1.remove(x) for x in l2] print l1 [1, 4, 5, 6]