from - a predefined python variable, storing the current module name, is called:
¿Cómo compartir variables a través de scripts en python? (9)
En su ejemplo, el primer script se ejecuta hasta su finalización, y luego el segundo script se ejecuta. Eso significa que necesitas algún tipo de estado persistente. Otras respuestas han sugerido el uso de archivos de texto o el módulo pickle
de Python. Personalmente soy perezoso, y no usaría un archivo de texto cuando podría usar pickle
; ¿Por qué debo escribir un analizador para analizar mi propio formato de archivo de texto?
En lugar de pickle
, también puede usar el módulo json
para almacenarlo como JSON. Esto podría ser preferible si desea compartir los datos con programas que no sean de Python, ya que JSON es un estándar simple y común. Si tu Python no tiene json
, obtén simplejson json
.
Si sus necesidades van más allá de pickle
o json
, digamos que en realidad desea tener dos programas de Python ejecutándose al mismo tiempo y actualizando las variables de estado persistentes en tiempo real, le sugiero que use la SQLite datos SQLite . Utilice un ORM para abstraer la base de datos, y es muy fácil. Para SQLite y Python, recomiendo Autumn ORM .
Lo siguiente no funciona
one.py
import shared
shared.value = ''Hello''
raw_input(''A cheap way to keep process alive..'')
dos.py
import shared
print shared.value
Ejecutar en dos líneas de comando como:
>>python one.py
>>python two.py
(El segundo recibe un error de atributo, con razón).
¿Hay alguna forma de lograr esto, es decir, compartir una variable entre dos scripts?
Es necesario almacenar la variable en algún tipo de archivo persistente. Hay varios módulos para hacer esto, dependiendo de su necesidad exacta.
El módulo pickle y cPickle puede guardar y cargar la mayoría de los objetos de Python en un archivo.
El módulo de almacenamiento puede almacenar objetos de python en una estructura similar a un diccionario (utilizando pickle detrás de escena).
Los módulos dbm / bsddb / dbhash / gdm pueden almacenar variables de cadena en una estructura similar a un diccionario.
El módulo sqlite3 puede almacenar datos en una base de datos SQL ligera.
El mayor problema con la mayoría de estos es que no están sincronizados a través de diferentes procesos: si un proceso lee un valor mientras otro escribe en el almacén de datos, es posible que obtenga datos incorrectos o que se dañen. Para solucionar esto, deberá escribir su propio mecanismo de bloqueo de archivos o usar una base de datos completa.
Espero que esté bien anotar mis notas sobre este tema aquí.
En primer lugar, aprecio mucho el ejemplo en el OP, porque ahí es también donde comencé, aunque me hizo pensar que shared
era un módulo Python incorporado, hasta que encontré un ejemplo completo en [Tutor] Variables globales entre Módulos ?? .
Sin embargo, cuando busqué "compartir variables entre scripts" (o procesos), además del caso cuando un script de Python necesita usar variables definidas en otros archivos de origen de Python (pero no necesariamente procesos en ejecución), me topé con otros dos casos de uso. :
- Un script se divide en varios procesos secundarios, que luego se ejecutan en paralelo (posiblemente en varios procesadores) en la misma PC
- Un script genera otros procesos secundarios, que luego se ejecutan en paralelo (posiblemente en varios procesadores) en la misma PC
Como tal, la mayoría de los éxitos relacionados con "variables compartidas" y "comunicación entre procesos" (IPC) tratan casos como estos dos; sin embargo, en ambos casos se puede observar un "padre", al cual los "hijos" usualmente tienen una referencia.
Sin embargo, lo que me interesa es ejecutar múltiples invocaciones del mismo script, ejecutarse de forma independiente y compartir datos entre ellos (como en Python: cómo compartir una instancia de objeto a través de múltiples invocaciones de un script ), en una instancia única / única modo. Ese tipo de problema no se resuelve realmente en los dos casos anteriores, sino que se reduce esencialmente al ejemplo en OP (compartir variables en dos scripts).
Ahora, cuando se trata de este problema en Perl, hay IPC::Shareable ; que "le permite vincular una variable a la memoria compartida", utilizando "un número entero o una cadena de 4 caracteres [1] que sirve como un identificador común para los datos en todo el espacio de proceso". Por lo tanto, no hay archivos temporales ni configuraciones de red, lo que me parece excelente para mi caso de uso; Así que estaba buscando lo mismo en Python.
Sin embargo, como responde la respuesta de @Drewfer, señala: " No podrá hacer lo que quiera sin almacenar la información en un lugar externo a las dos instancias del intérprete "; o en otras palabras: o tienes que usar una configuración de red / socket - o tienes que usar archivos temporales (ergo, no hay RAM compartida para " sesiones de python totalmente separadas ").
Ahora, incluso con estas consideraciones, es un poco difícil encontrar ejemplos de trabajo (a excepción de pickle
), también en los documentos para mmap y multiprocessing . He logrado encontrar algunos otros ejemplos, que también describen algunos escollos que los documentos no mencionan:
- Uso de
mmap
: código de trabajo en dos scripts diferentes en Compartir datos de Python entre procesos usando mmap | el blog de schmichael- Demuestra cómo ambos scripts cambian el valor compartido
- Tenga en cuenta que aquí se crea un archivo temporal como almacenamiento para los datos guardados:
mmap
es solo una interfaz especial para acceder a este archivo temporal
- Uso de
multiprocessing
: código de trabajo en:- Python multiprocessing RemoteManager bajo multiprocessing.Process : ejemplo funcional de
SyncManager
(a través demanager.start()
) conQueue
compartida; Escrituras de servidor (s), lectura de clientes (datos compartidos) - ¿Comparación del módulo multiprocesamiento y pyro? - ejemplo de trabajo de
BaseManager
(a través deserver.serve_forever()
) con clase personalizada compartida; Servidor escribe, cliente lee y escribe - Cómo sincronizar un dict de Python con multiprocesamiento : esta respuesta tiene una gran explicación de
multiprocessing
trampas delmultiprocessing
, y es un ejemplo funcional deSyncManager
(a través demanager.start()
) con dict compartido; El servidor no hace nada, el cliente lee y escribe.
- Python multiprocessing RemoteManager bajo multiprocessing.Process : ejemplo funcional de
Gracias a estos ejemplos, se me ocurrió un ejemplo, que esencialmente hace lo mismo que el ejemplo mmap
, con enfoques del ejemplo " sincronizar un BaseManager
Python ": usar BaseManager
(a través de manager.start()
través de la dirección de ruta de archivo) con una dirección compartida. lista; Tanto el servidor como el cliente leen y escriben (pegados a continuación). Tenga en cuenta que:
-
multiprocessing
gestores demultiprocessing
se pueden iniciar a través demanager.start()
oserver.serve_forever()
-
serve_forever()
bloquea -start()
no lo hace - Hay una función de registro automático en el
multiprocessing
: parece funcionar bien con los procesos destart()
ed, pero parece ignorar los queserve_forever()
-
- La especificación de la dirección en el
multiprocessing
puede ser IP (socket) o archivo temporal (¿posiblemente una tubería?) Ruta; en documentos demultiprocessing
:- La mayoría de los ejemplos utilizan
multiprocessing.Manager()
: esta es solo una función ( no unaSyncManager
clase) que devuelve unSyncManager
, que es una subclase especial deBaseManager
; y usastart()
, pero no para IPC entre scripts ejecutados de forma independiente; Aquí se utiliza una ruta de archivo - Algunos otros ejemplos de enfoque de
serve_forever()
para IPC entre scripts ejecutados de forma independiente; Aquí se utiliza la dirección IP / socket - Si no se especifica una dirección, entonces se usa automáticamente una ruta de archivo temporal (vea 16.6.2.12. Registrando un ejemplo de cómo ver esto)
- La mayoría de los ejemplos utilizan
Además de todos los escollos en la publicación " sincronizar un dict de python ", hay otros adicionales en el caso de una lista. Ese post notas:
Todas las manipulaciones del dict deben realizarse con métodos y no con asignaciones de dict (syncdict ["blast"] = 2 fallará miserablemente debido a la forma en que el multiprocesamiento comparte objetos personalizados)
La solución para obtener y configurar dict[''key'']
, es el uso de los métodos públicos de dict
get
y update
. El problema es que no existen métodos públicos como alternativa para la list[index]
; por lo tanto, para una lista compartida, además tenemos que registrar los métodos __getitem__
y __setitem__
(que son privados para la list
) como están exposed
, lo que significa que también tenemos que volver a registrar todos los métodos públicos para la list
también :/
Bueno, creo que esas fueron las cosas más críticas; estos son los dos scripts: solo se pueden ejecutar en terminales separados (servidor primero); nota desarrollada en Linux con Python 2.7:
a.py
(servidor):
import multiprocessing
import multiprocessing.managers
import logging
logger = multiprocessing.log_to_stderr()
logger.setLevel(logging.INFO)
class MyListManager(multiprocessing.managers.BaseManager):
pass
syncarr = []
def get_arr():
return syncarr
def main():
# print dir([]) # cannot do `exposed = dir([])`!! manually:
MyListManager.register("syncarr", get_arr, exposed=[''__getitem__'', ''__setitem__'', ''__str__'', ''append'', ''count'', ''extend'', ''index'', ''insert'', ''pop'', ''remove'', ''reverse'', ''sort''])
manager = MyListManager(address=(''/tmp/mypipe''), authkey='''')
manager.start()
# we don''t use the same name as `syncarr` here (although we could);
# just to see that `syncarr_tmp` is actually <AutoProxy[syncarr] object>
# so we also have to expose `__str__` method in order to print its list values!
syncarr_tmp = manager.syncarr()
print("syncarr (master):", syncarr, "syncarr_tmp:", syncarr_tmp)
print("syncarr initial:", syncarr_tmp.__str__())
syncarr_tmp.append(140)
syncarr_tmp.append("hello")
print("syncarr set:", str(syncarr_tmp))
raw_input(''Now run b.py and press ENTER'')
print
print ''Changing [0]''
syncarr_tmp.__setitem__(0, 250)
print ''Changing [1]''
syncarr_tmp.__setitem__(1, "foo")
new_i = raw_input(''Enter a new int value for [0]: '')
syncarr_tmp.__setitem__(0, int(new_i))
raw_input("Press any key (NOT Ctrl-C!) to kill server (but kill client first)".center(50, "-"))
manager.shutdown()
if __name__ == ''__main__'':
main()
b.py
(cliente)
import time
import multiprocessing
import multiprocessing.managers
import logging
logger = multiprocessing.log_to_stderr()
logger.setLevel(logging.INFO)
class MyListManager(multiprocessing.managers.BaseManager):
pass
MyListManager.register("syncarr")
def main():
manager = MyListManager(address=(''/tmp/mypipe''), authkey='''')
manager.connect()
syncarr = manager.syncarr()
print "arr = %s" % (dir(syncarr))
# note here we need not bother with __str__
# syncarr can be printed as a list without a problem:
print "List at start:", syncarr
print "Changing from client"
syncarr.append(30)
print "List now:", syncarr
o0 = None
o1 = None
while 1:
new_0 = syncarr.__getitem__(0) # syncarr[0]
new_1 = syncarr.__getitem__(1) # syncarr[1]
if o0 != new_0 or o1 != new_1:
print ''o0: %s => %s'' % (str(o0), str(new_0))
print ''o1: %s => %s'' % (str(o1), str(new_1))
print "List is:", syncarr
print ''Press Ctrl-C to exit''
o0 = new_0
o1 = new_1
time.sleep(1)
if __name__ == ''__main__'':
main()
Como observación final, en Linux /tmp/mypipe
se crea, pero tiene 0 bytes y tiene los atributos srwxr-xr-x
(para un socket); Supongo que esto me hace feliz, ya que no tengo que preocuparme por los puertos de red, ni por los archivos temporales como tales :)
Otras preguntas relacionadas:
- Python: es posible compartir datos en memoria entre 2 procesos separados (muy buena explicación)
- Eficiente Python a Python IPC
- Python: enviando una variable a otro script
Lo que intenta hacer aquí (almacenar un estado compartido en un módulo de Python a través de intérpretes de Python separados) no funcionará.
Un valor en un módulo puede ser actualizado por un módulo y luego leído por otro módulo, pero esto debe estar dentro del mismo intérprete de Python. Lo que parece que está haciendo aquí es en realidad una especie de comunicación entre procesos; Esto podría lograrse mediante la comunicación de socket entre los dos procesos, pero es significativamente menos trivial de lo que se espera que funcione aquí.
No podrá hacer lo que quiera sin almacenar la información en un lugar externo a las dos instancias del intérprete.
Si solo desea las variables simples, puede volcar fácilmente un dict de python en un archivo con el módulo pickle en el script uno y luego volver a cargarlo en el script dos. Ejemplo:
one.py
import pickle
shared = {"Foo":"Bar", "Parrot":"Dead"}
fp = open("shared.pkl","w")
pickle.dump(shared, fp)
dos.py
import pickle
fp = open("shared.pkl")
shared = pickle.load(fp)
print shared["Foo"]
Puede utilizar el archivo mmap relativo simple. puede utilizar el shared.py para almacenar las constantes comunes. El siguiente código funcionará en diferentes intérpretes de Python / scripts / procesos
shared.py:
MMAP_SIZE = 16*1024
MMAP_NAME = ''Global//SHARED_MMAP_NAME''
* El "Global" es la sintaxis de Windows para los nombres globales
one.py:
from shared import MMAP_SIZE,MMAP_NAME
def write_to_mmap():
map_file = mmap.mmap(-1,MMAP_SIZE,tagname=MMAP_NAME,access=mmap.ACCESS_WRITE)
map_file.seek(0)
map_file.write(''hello/n'')
ret = map_file.flush() != 0
if sys.platform.startswith(''win''):
assert(ret != 0)
else:
assert(ret == 0)
dos.py:
from shared import MMAP_SIZE,MMAP_NAME
def read_from_mmap():
map_file = mmap.mmap(-1,MMAP_SIZE,tagname=MMAP_NAME,access=mmap.ACCESS_READ)
map_file.seek(0)
data = map_file.readline().rstrip(''/n'')
map_file.close()
print data
* Este código fue escrito para Windows, Linux puede necesitar pequeños ajustes
más información en - https://docs.python.org/2/library/mmap.html
Te aconsejo que uses el módulo multiprocessing . No puede ejecutar dos scripts desde la línea de comandos, pero puede hacer que dos procesos separados se hablen fácilmente.
De los ejemplos del doc.
from multiprocessing import Process, Queue
def f(q):
q.put([42, None, ''hello''])
if __name__ == ''__main__'':
q = Queue()
p = Process(target=f, args=(q,))
p.start()
print q.get() # prints "[42, None, ''hello'']"
p.join()
Utilizar archivos de texto o variables de entorno. Ya que los dos se ejecutan por separado, realmente no puedes hacer lo que estás tratando de hacer.
sudo apt-get install memcached python-memcache
one.py
import memcache
shared = memcache.Client([''127.0.0.1:11211''], debug=0)
shared.set(''Value'', ''Hello'')
dos.py
import memcache
shared = memcache.Client([''127.0.0.1:11211''], debug=0)
print shared.get(''Value'')