2010-10-09 14 views

Respuesta

7
[x for x in lst if fn(x) != 0] 

Esta es una lista de "comprensión", uno de los mejores piezas de azúcar sintáctica que a menudo lleva a líneas de código en otros idiomas y declaraciones de variables adicionales de Python, etc.

Ver: http://docs.python.org/tutorial/datastructures.html#list-comprehensions

+4

Tenga en cuenta que esto crea una nueva lista; no muta una lista existente. – intuited

+4

'if not fn (x)' es más idiomático y funciona correctamente con cualquier valor que Python considere verdadero/falso (su función da por resultado 0 o 1, pero no hay ninguna razón para codificar esta suposición). –

1

edit: vea la parte inferior para obtener la mejor respuesta.

Si necesita mutar una lista existente, por ejemplo porque tiene otra referencia en otro lugar, necesitará realmente remove los valores de la lista.

No estoy al tanto de cualquier función en Python, pero algo como esto iba a funcionar (código no probado):

def cull_list(lst, pred): 
    """Removes all values from ``lst`` which for which ``pred(v)`` is false.""" 
    def remove_all(v): 
     """Remove all instances of ``v`` from ``lst``""" 
     try: 
      while True: 
       lst.remove(v) 
     except ValueError: 
      pass 

    values = set(lst) 

    for v in values: 
     if not pred(v): 
      remove_all(v) 

Una alternativa probablemente más eficientes que pueden parecer un poco demasiado como código C para el gusto de algunas personas:

def efficient_cull_list(lst, pred): 
    end = len(lst) 
    i = 0 
    while i < end: 
     if not pred(lst[i]): 
      del lst[i] 
      end -= 1 
     else: 
      i += 1 

edición ...: Aaron como señaló en los comentarios, esto se puede hacer mucho más limpiamente con algo como

def reversed_cull_list(lst, pred): 
    for i in range(len(lst) - 1, -1, -1): 
     if not pred(lst[i]): 
      del lst[i] 

... editar

El truco con estas rutinas es que el uso de una función como enumerate, según lo sugerido por otro (s) respuesta (s), no se tendrá en cuenta el hecho de que los elementos de la lista ha sido eliminada. La única forma (que yo sepa) de hacer eso es simplemente rastrear el índice manualmente en lugar de permitir que python haga la iteración. Hay muchas posibilidades de encontrar un compromiso velocidad de allí, por lo que puede llegar a ser mejor sólo para hacer algo como

lst[:] = (v for v in lst if pred(v)) 

realidad, ahora que lo pienso, esto es, con mucho, la forma más sensata de hacer un 'en -place 'filter en una lista. Los valores del generador se repiten antes de llenar los elementos de lst, por lo que no hay problemas de conflicto de índice. Si quieres que esto sea más explícita acaba de hacer

lst[:] = [v for v in lst if pred(v)] 

Yo no creo que vaya a hacer mucha diferencia en este caso, en términos de eficiencia.

Cualquiera de estos dos últimos enfoques, si entiendo correctamente cómo funcionan realmente, hacer una copia extra de la lista, por lo que una de las soluciones de buena fe in situ mencionadas anteriormente sería mejor si se trata de algunas "grandes extensiones de tierra".

+0

'enumerate' parece ajustar los índices de forma apropiada. No pensé que fuera así hasta que lo intenté. Escriba un bucle simple sobre una lista del 1 al 20 que elimine cada elemento que sea un múltiplo par de 3. Imprima la lista y el índice cada vez que realice una eliminación. Eliminará los índices 2, 4, 5, 6, 10 y 12. –

+0

Puede simplificar el ciclo haciendo un bucle sobre él al revés. Luego, cuando eliminas un elemento, no tienes que decrementar (o incluso tener) 'end'. Además, no hay rama en el tiempo. – aaronasterling

+0

@ D.Shawley: O estás confundido o lo estoy. Escribí algunos [doctests] (http://gist.github.com/618883) para probar las diversas soluciones presentadas a esta pregunta; tal vez tengo algo mal con eso? – intuited

3

Usaría una expresión de generador sobre una lista de comprensión para evitar una lista potencialmente grande e intermedia.

result = (x for x in l if f(x)) 
# print it, or something 
print list(result) 

Al igual que una lista por comprensión, esto no va a modificar la lista original, en su lugar.

0
>>> s = [1, 2, 3, 4, 5, 6] 
>>> def f(x):    
...  if x<=2: return 0 
...  else: return 1 
>>> for n,x in enumerate(s): 
...  if f(x) == 0: s[n]=None 
>>> s=filter(None,s) 
>>> s 
[3, 4, 5, 6] 
+0

Esto no funciona: para la lista '[1, 2, 3, 4, 5, 6]' y 'f = lambda v: v <= 2' obtengo' [1, 2, 4, 6] ' . – intuited

0

Con una expresión generadora:

alist[:] = (item for item in alist if afunction(item)) 

Funcional:

alist[:] = filter(afunction, alist) 

o:

import itertools 
alist[:] = itertools.ifilter(afunction, alist) 

Todos equivalente.

También puede utilizar una lista de comprensión:

alist = [item for item in alist if afunction(item)] 

una actualización in situ modificación:

import collections 

indexes_to_delete= collections.deque(
    idx 
    for idx, item in enumerate(alist) 
    if afunction(item)) 
while indexes_to_delete: 
    del alist[indexes_to_delete.pop()] 
Cuestiones relacionadas