xticks barplot python list-comprehension generator

python - barplot - Expresiones del generador vs. Comprensión de lista



pandas plot (8)

A veces, puede salirse con la función tee de itertools , devuelve múltiples iteradores para el mismo generador que se pueden usar de forma independiente.

¿Cuándo debería usar las expresiones generadoras y cuándo debería usar la lista de comprensión en Python?

# Generator expression (x*2 for x in range(256)) # List comprehension [x*2 for x in range(256)]


Al crear un generador a partir de un objeto mutable (como una lista), tenga en cuenta que el generador se evaluará en el estado de la lista en el momento de usar el generador, no en el momento de la creación del generador:

>>> mylist = ["a", "b", "c"] >>> gen = (elem + "1" for elem in mylist) >>> mylist.clear() >>> for x in gen: print (x) # nothing

Si existe alguna posibilidad de que su lista se modifique (o un objeto mutable dentro de esa lista) pero necesita el estado en la creación del generador, necesita usar una lista de comprensión.


El beneficio de una expresión generadora es que usa menos memoria, ya que no construye toda la lista a la vez. Las expresiones generadoras se utilizan mejor cuando la lista es un intermediario, como sumar los resultados o crear un dictado de los resultados.

Por ejemplo:

sum(x*2 for x in xrange(256)) dict( ((k, some_func(k) for k in some_list_of_keys) )

La ventaja es que la lista no se genera completamente, y por lo tanto se usa poca memoria (y también debería ser más rápida)

Sin embargo, debe utilizar las listas de comprensión cuando el producto final deseado es una lista. No va a guardar ninguna memoria utilizando expresiones generadoras, ya que desea la lista generada. También obtiene la ventaja de poder utilizar cualquiera de las funciones de la lista como ordenadas o invertidas.

Por ejemplo:

reversed( [x*2 for x in xrange(256)] )


El punto importante es que la lista de comprensión crea una nueva lista. El generador crea un objeto iterable que "filtrará" el material de origen sobre la marcha a medida que consume los bits.

Imagina que tienes un archivo de registro de 2TB llamado "hugefile.txt", y quieres el contenido y la longitud de todas las líneas que comienzan con la palabra "ENTRY".

Así que intenta comenzar escribiendo una lista de comprensión:

logfile = open("hugefile.txt","r") entry_lines = [(line,len(line)) for line in logfile if line.startswith("ENTRY")]

Esto absorbe todo el archivo, procesa cada línea y almacena las líneas coincidentes en su matriz. Por lo tanto, esta matriz podría contener hasta 2 TB de contenido. Eso es un montón de RAM, y probablemente no sea práctico para sus propósitos.

Entonces, en cambio, podemos usar un generador para aplicar un "filtro" a nuestro contenido. En realidad, no se lee ningún dato hasta que comenzamos a iterar sobre el resultado.

logfile = open("hugefile.txt","r") entry_lines = ((line,len(line)) for line in logfile if line.startswith("ENTRY"))

Ni siquiera una sola línea ha sido leída de nuestro archivo todavía. De hecho, digamos que queremos filtrar nuestro resultado aún más:

long_entries = ((line,length) for (line,length) in entry_lines if length > 80)

Aún no se ha leído nada, pero ahora hemos especificado dos generadores que actuarán sobre nuestros datos como deseamos.

Vamos a escribir nuestras líneas filtradas a otro archivo:

outfile = open("filtered.txt","a") for entry,length in long_entries: outfile.write(entry)

Ahora leemos el archivo de entrada. A medida que nuestro bucle for sigue solicitando líneas adicionales, el generador long_entries exige líneas del generador de entry_lines , devolviendo solo aquellas cuya longitud es mayor de 80 caracteres. Y a su vez, el generador entry_lines solicita líneas (filtradas como se indica) desde el iterador del logfile , que a su vez lee el archivo.

Por lo tanto, en lugar de "enviar" datos a su función de salida en forma de una lista completa, le está dando a la función de salida una forma de "extraer" datos solo cuando es necesario. Esto es en nuestro caso mucho más eficiente, pero no tan flexible. Los generadores son unidireccionales, una pasada; los datos del archivo de registro que hemos leído se descartan inmediatamente, por lo que no podemos volver a una línea anterior. Por otro lado, no tenemos que preocuparnos por mantener los datos una vez que hayamos terminado con ellos.


Estoy usando el módulo de picadillo de Hadoop . Creo que este es un gran ejemplo para tomar nota de:

import mincemeat def mapfn(k,v): for w in v: yield ''sum'',w #yield ''count'',1 def reducefn(k,v): r1=sum(v) r2=len(v) print r2 m=r1/r2 std=0 for i in range(r2): std+=pow(abs(v[i]-m),2) res=pow((std/r2),0.5) return r1,r2,res

Aquí, el generador obtiene números de un archivo de texto (tan grande como 15 GB) y aplica cálculos simples a esos números usando el mapa reducido de Hadoop. Si no hubiera usado la función de rendimiento, sino una comprensión de la lista, habría tomado mucho más tiempo calcular las sumas y el promedio (sin mencionar la complejidad del espacio).

Hadoop es un gran ejemplo para usar todas las ventajas de los generadores.


Iterar sobre la expresión del generador o la comprensión de la lista hará lo mismo. Sin embargo, la lista de comprensión creará la lista completa en la memoria primero, mientras que la expresión del generador creará los elementos sobre la marcha, por lo que podrá usarlos para secuencias muy grandes (¡y también infinitas!).


La respuesta de John es buena (esa comprensión de la lista es mejor cuando se quiere iterar sobre algo varias veces). Sin embargo, también vale la pena señalar que debe utilizar una lista si desea utilizar cualquiera de los métodos de la lista. Por ejemplo, el siguiente código no funcionará:

def gen(): return (something for something in get_some_stuff()) print gen()[:2] # generators don''t support indexing or slicing print [5,6] + gen() # generators can''t be added to lists

Básicamente, use una expresión generadora si todo lo que está haciendo es iterar una vez. Si desea almacenar y usar los resultados generados, entonces probablemente esté mejor con una lista de comprensión.

Dado que el rendimiento es la razón más común para elegir uno sobre el otro, mi consejo es que no se preocupe por eso y simplemente elija uno; Si descubre que su programa se está ejecutando con demasiada lentitud, solo entonces debería regresar y preocuparse por ajustar su código.


Use la lista de comprensión cuando el resultado deba repetirse varias veces, o cuando la velocidad sea primordial. Use expresiones generadoras donde el rango sea grande o infinito.