2012-03-17 6 views
7

... y cada vuelta parecía una lista de comprensión.Me encontré balanceando la lista de comprensión del martillo

En lugar de:

for stuff in all_stuff: 
    do(stuff) 

que estaba haciendo (no asignar la lista para cualquier cosa):

[ do(stuff) for stuff in all_stuff ] 

Este es un patrón común que se encuentra en list-comp how-to's. 1) OK, entonces no hay problema, ¿no? Incorrecto. 2) ¿No puede ser solo un estilo de código? Súper mal.

1) Sí, eso estuvo mal. Como señala NiklasB, el de los HowTos es construir una nueva lista.

2) Tal vez, pero no es obvio y explícito, así que mejor no usarlo.

No tuve en cuenta que esos procedimientos estaban en su mayoría basados ​​en línea de comandos. Después de que mi equipo me gritara preguntándome por qué demonios estaba formando listas masivas y luego dejándolas ir, se me ocurrió que podría estar presentando un error importante relacionado con la memoria.

Así que aquí está mi pregunta/s. Si tuviera que hacer esto en un proceso de ejecución muy largo, donde se consumían muchos datos, ¿esta "lista" simplemente continuaría consumiendo mi memoria hasta que la dejaran ir? ¿Cuándo recuperará el recolector de basura la memoria? ¿Después de perder el alcance de esta lista?

Supongo que sí, seguirá consumiendo mi memoria. No sé cómo funciona el recolector de basura de pitón, pero me atrevería a decir que esta lista existirá hasta después de que se llame al next en all_stuff.

EDITAR.

La esencia de mi pregunta es transmitida mucho más limpias in this question (gracias por el enlace Niklas)

Respuesta

6

Si tuviera que hacer esto en un proceso muy larga duración, donde se consumía gran cantidad de datos, que esta "lista" simplemente seguir consumiendo mi memoria hasta dejar ir ?

Absolutamente.

¿Cuándo recuperará el recolector de basura la memoria? ¿Después de perder el alcance de esta lista?

CPython utiliza el recuento de referencias, por lo que es el caso más probable. Otras implementaciones funcionan de manera diferente, así que no cuentes con eso.

Gracias a Karl por señalar que, debido a los complejos mecanismos de gestión de memoria utilizados por CPython, esto significa que no significa que la memoria se devuelve inmediatamente al sistema operativo después de eso.

No sé cómo funciona el recolector de basura python, pero me atrevería a decir que esta lista existirá hasta después de que se llame a all_stuff la última.

No creo cualquier recolector de basura funciona de esa manera. Por lo general, marcan y barrido, por lo que podría pasar bastante tiempo antes de que la lista sea recolectada.

Este es un patrón común que se encuentra en el listado de comp-instrucciones.

Absolutamente no. El punto es que usted itera la lista con el propósito de hacer algo con cada elemento (se llama al do para que sea side-effects). En todos los ejemplos de List-comp HOWTO, la lista se itera en construye una nueva lista basada en los elementos del anterior. Veamos un ejemplo:

# list comp, creates the list [0,1,2,3,4,5,6,7,8,9] 
[i for i in range(10)] 

# loop, does nothing 
for i in range(10): 
    i # meh, just an expression which doesn't have an effect 

Tal vez usted estará de acuerdo en que este bucle es totalmente sin sentido, ya que no hace nada, al contrario de la comprensión, que construye una lista. En su ejemplo, es al revés: la comprensión no tiene sentido, ¡porque no necesita la lista! Puede encontrar más información sobre el problema en un related question

Por cierto, si realmente desea escribir ese bucle en una línea, use un consumidor de generador como deque.extend. Este será ligeramente más lento que un for bucle prima en este ejemplo simple, sin embargo:

>>> from collections import deque 
>>> consume = deque(maxlen=0).extend 
>>> consume(do(stuff) for stuff in all_stuff) 
+0

¿Puede usted hacer algunos puntos de referencia 'timeit' para su último código ¿bloquear? – Blender

+0

@Blender: Meh, parece que no puede probar esto ... Gracias por obligarme a aprenderlo de la manera difícil: P –

+0

Hubo una pregunta hace algún tiempo sobre eso: [Pasar los iteradores a cualquiera para la ejecución de velocidad y ¿Por qué?] (http://stackoverflow.com/q/9144934/1132524) –

3

Trate de hacer manualmente GC y el vertido de las estadísticas.

gc.DEBUG_STATS

estadísticas de impresión durante la recogida. Esta información puede ser útil al ajustar la frecuencia de recolección.

DE

http://docs.python.org/library/gc.html

2

El CPython GC cosechará una vez que no hay referencias a ella fuera de un ciclo. Jython e IronPython siguen las reglas de los GC subyacentes.

0

No sé cómo funciona el recolector de basura python, pero me atrevo a decir que esta lista existirá hasta después de que se llame a all_stuff la última.

Bueno, por supuesto que lo hará, ya que está compilando una lista que tendrá el mismo número de elementos de all_stuff. El intérprete no puede descartar la lista antes de que esté terminada, ¿o sí? Puede llamar al gc.collect entre uno de estos bucles y otro, pero cada uno estará completamente construido antes de que pueda recuperarse.

En algunos casos se puede utilizar un generador de expresión en lugar de una lista por comprensión, por lo que no tiene que crear una lista con todos sus valores:

(do_something(i) for i in xrange(1000)) 

Sin embargo usted todavía tiene que "exaust "ese generador de alguna manera ...

+0

Ese era el problema, all_stuff era un generador que generaba datos de red. No iba a agotarse pronto. – sbartell

+0

Quería decir que tenía que asegurarse de que el intérprete repitiera el generador (lo siento, mi inglés). El uso de una de las sugerencias de otras personas, como 'any' o' deque.extend' consumiría cada elemento tan pronto como se generen, sin almacenarlos en una lista. – mgibsonbr

2

Si te gusta ese idioma, do vuelve algo que siempre evalúa como Verdadero o Falso y considerarían una alternativa similar sin efectos secundarios desagradables, se puede utilizar una expresión generadora combinada con any o all.

Para las funciones que devuelven valores falsos (o no devuelven):

any(do(stuff) for stuff in all_stuff) 

Para las funciones que devuelven valores verdaderos:

all(do(stuff) for stuff in all_stuff) 
+2

A menos que 'do' tenga un valor de retorno significativo que simplemente no se está mirando aquí. 'any' solo agota el iterador hasta el primer valor True que ceda. – lvc

+0

Muy cierto. Mi mal, gracias por señalar eso. –

+0

Tenga en cuenta que a veces hay ventajas de rendimiento (al menos con CPython) con este método frente a un bucle normal. – agf

Cuestiones relacionadas