2011-07-14 26 views
35

¿Hay alguna forma de mantener una variable de resultado en la memoria para no tener que volver a calcularla cada vez que ejecuto el script? Estoy haciendo una serie larga (5-10 seg) de las operaciones exactas en un conjunto de datos (que estoy leyendo del disco) cada vez que ejecuto mi script. Esto no sería un gran problema ya que soy bastante bueno en el uso del editor interactivo para depurar mi código entre ejecuciones; sin embargo, a veces las capacidades interactivas simplemente no lo cortan.Mantener variables persistentes en la memoria entre las ejecuciones de la secuencia de comandos de Python

Sé que podría escribir mis resultados en un archivo en el disco, pero me gustaría evitar hacerlo si es posible. Esta debería ser una solución que genere una variable la primera vez que ejecute el script, y lo mantenga en la memoria hasta que el propio shell se cierre o hasta que explícitamente diga que se desvanezca. Algo como esto:

# Check if variable already created this session 
in_mem = var_in_memory() # Returns pointer to var, or False if not in memory yet 
if not in_mem: 
    # Read data set from disk 
    with open('mydata', 'r') as in_handle: 
     mytext = in_handle.read() 
    # Extract relevant results from data set 
    mydata = parse_data(mytext) 
    result = initial_operations(mydata) 
    in_mem = store_persistent(result) 

tengo una idea de que el módulo shelve podría ser lo que estoy buscando aquí, pero parece que con el fin de abrir una variable estantería que tendría que especificar un nombre de archivo para el objeto persistente, por lo que no estoy seguro de si es bastante lo que estoy buscando.

¿Algún consejo sobre hacer que shelve haga lo que quiero que haga? ¿Alguna idea alternativa?

Respuesta

29

Puede lograr algo como esto utilizando la función global reload para volver a ejecutar el código del script principal. Tendrá que escribir un script contenedor que importe su script principal, le pida la variable que quiere almacenar en caché, almacena una copia en caché dentro del alcance del módulo del script contenedor y luego cuando lo desee (cuando presione ENTER en stdin o lo que sea), llama al reload(yourscriptmodule) pero esta vez pasa el objeto almacenado en caché de manera que yourscript puede eludir el costoso cálculo. Aquí hay un ejemplo rápido.

wrapper.py

import sys 
import mainscript 

part1Cache = None 
if __name__ == "__main__": 
    while True: 
     if not part1Cache: 
      part1Cache = mainscript.part1() 
     mainscript.part2(part1Cache) 
     print "Press enter to re-run the script, CTRL-C to exit" 
     sys.stdin.readline() 
     reload(mainscript) 

mainscript.py

def part1(): 
    print "part1 expensive computation running" 
    return "This was expensive to compute" 

def part2(value): 
    print "part2 running with %s" % value 

Mientras wrapper.py se está ejecutando, puede editar mainscript.py, agrega nuevo código a la función part2 y ser capaz de funcionar su nuevo código contra el part1Cache precalculado.

+3

Consideraría agregar un manejador de excepciones, donde ejecuta la fuente externa. – mehmetminanc

+1

¿Qué sucede si se actualiza una dependencia de mainscript.py? ¿Debo volver a cargarlo de forma explícita? – pomber

+0

¿No sería suficiente almacenar la variable en os.environ? – Ladmerc

3

Python's shelve es una solución de persistencia para objetos en escabeche (serializados) y está basada en archivos. La ventaja es que almacena objetos de Python directamente, lo que significa que la API es bastante simple.

Si realmente desea evitar el disco, la tecnología que está buscando es una "base de datos en memoria". Existen varias alternativas, vea esta pregunta SO: in-memory database in Python.

+1

Gracias, se ve bien para otras aplicaciones pero parece demasiado poderoso para mis necesidades. –

4

Para mantener los datos en la memoria, el proceso debe seguir ejecutándose. La memoria pertenece al proceso que ejecuta el script, NO al shell. El shell no puede contener memoria para ti.

Así que si quiere cambiar su código y mantener su proceso en ejecución, tendrá que volver a cargar los módulos cuando se modifiquen. Si alguno de los datos en la memoria es una instancia de una clase que cambia, deberá encontrar una forma de convertirlo a una instancia de la nueva clase. Es un desastre. No hay muchos idiomas que sean buenos para este tipo de parches (le viene a la mente Common Lisp), y hay muchas posibilidades de que las cosas salgan mal.

+0

Muchas gracias por la respuesta informativa. Es bueno saber por qué una solución en particular no funciona como yo quiero, agradezco su explicación. –

5

Si solo quiere persistir un objeto (o un gráfico de objetos) para sesiones futuras, el módulo de estantería probablemente sea excesivo. Simplemente recoja el objeto que le importa. Haga el trabajo y guarde la salmuera si no tiene un archivo encurtido, o cargue el archivo de encurtido si tiene uno.

import os 
import cPickle as pickle 

pickle_filepath = "/path/to/picklefile.pickle" 

if not os.path.exists(pickle_filepath): 
    # Read data set from disk 
    with open('mydata', 'r') as in_handle: 
     mytext = in_handle.read() 
    # Extract relevant results from data set 
    mydata = parse_data(mytext) 
    result = initial_operations(mydata) 
    with open(pickle_filepath, 'w') as pickle_handle: 
     pickle.dump(result, pickle_handle) 
else: 
    with open(pickle_filepath) as pickle_handle: 
     result = pickle.load(pickle_handle) 
+0

Pickle también tiende a ser más rápido que shelve – pufferfish

0

Puede hacer esto, pero debe utilizar un terminal de Python. En otras palabras, el shell que usa para iniciar los scripts de Python debe ser un proceso de Python. Entonces, cualquier variable o clase global vivirá hasta que cierre el shell.

Mire el módulo cmd que hace que sea fácil escribir un programa de shell. Incluso puede organizar para que cualquier comando que no esté implementado en su shell pase al shell del sistema para su ejecución (sin cerrar el shell). Entonces tendría que implementar algún tipo de comando, por ejemplo, prun, que ejecute un script de Python utilizando el módulo runpy.

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

Usted tendría que utilizar el parámetro init_globals para pasar sus datos especiales para el espacio de nombres del programa, lo ideal sería un diccionario o una sola instancia de clase.

1

Ésta es una solución depende OS ...

$mkfifo inpipe 

#/usr/bin/python3 
#firstprocess.py 
complicated_calculation() 
while True: 
with open('inpipe') as f: 
    try: 
    print(exec (f.read())) 
    except Exception as e: print(e) 

$./first_process.py & 
$cat second_process.py > inpipe 

Esto le permitirá cambiar y volver a definir variables en el primer proceso sin copiar o volver a calcular nada. Debe ser la solución más eficiente en comparación con multiproceso, memcached, pickle, shelve modules o bases de datos.

Esto es realmente bueno si quieres editar y redefinir second_process.py iterativamente en tu editor o IDE hasta que lo tengas bien sin tener que esperar al primer proceso (por ejemplo, inicializar un dict grande, etc.) para ejecutar cada hora de hacer un cambio

0

Puede ejecutar un script persistente en el servidor a través del sistema operativo que carga/calcs e incluso recarga y recalcula periódicamente los datos sql en estructuras de memoria y luego accede a los datos en memoria de su otro script a través de un enchufe.

Cuestiones relacionadas