42 Stimmen

Wie kann man in Python Variablen in verschiedenen Skripten gemeinsam nutzen?

Das Folgende funktioniert nicht

ein.py

import shared
shared.value = 'Hello'
raw_input('A cheap way to keep process alive..')

zwei.py

import shared
print shared.value

auf zwei Befehlszeilen als ausgeführt:

>>python one.py
>>python two.py

(der zweite erhält zu Recht einen Attributfehler).

Gibt es eine Möglichkeit, dies zu erreichen, d.h. eine Variable zwischen zwei Skripten zu teilen?

7 Stimmen

Meinen Sie, dass Sie einen Wert in zwei völlig getrennten Python-Sitzungen gemeinsam nutzen wollen? Wenn ja, ist das nicht möglich, indem Sie einfach einen Wert in einem gemeinsamen Modul setzen.

0 Stimmen

Ich danke Ihnen. Im Moment bin ich dabei, ein drittes Modul zu erstellen, das beide importiert, so dass alles in der gleichen Sitzung geschieht. Die Pickle-Lösung funktioniert nicht ganz für mich, weil ich eine threading.Condition()-Variable in einem Modul setzen wollte (die wegen der impliziten Sperre nicht gepickt werden kann) und sie von beiden Sitzungen aus verwenden wollte. Und jetzt, wo ich darüber nachdenke, ist die Möglichkeit, die Variable gemeinsam zu nutzen, wahrscheinlich das geringste meiner Probleme, da ich nicht glaube, dass der Zustand in der Bedingungsvariable gemeinsam genutzt werden wird.

0 Stimmen

Ich bin auf diese Frage gekommen, als ich das gleiche Problem hatte. Dann habe ich über Steckdosen gelesen. Warum sollte man sie nicht verwenden?

59voto

sdaau Punkte 34279

Ich hoffe, es ist in Ordnung, wenn ich meine Notizen zu diesem Thema hier niederschreibe.

Zunächst einmal schätze ich das Beispiel im OP sehr, denn damit habe ich auch angefangen - obwohl es mich zum Nachdenken gebracht hat shared ist ein eingebautes Python-Modul, bis ich ein vollständiges Beispiel unter [Tutor] Globale Variablen zwischen Modulen ?? .

Als ich jedoch nach der "gemeinsamen Nutzung von Variablen zwischen Skripten" (oder Prozessen) suchte - abgesehen von dem Fall, dass ein Python-Skript Variablen verwenden muss, die in anderen Python-Quelldateien (aber nicht unbedingt in laufenden Prozessen) definiert sind -, stieß ich hauptsächlich auf zwei andere Anwendungsfälle:

  • Ein Skript teilt sich selbst in mehrere untergeordnete Prozesse auf, die dann parallel (möglicherweise auf mehreren Prozessoren) auf demselben PC laufen
  • Ein Skript legt mehrere andere untergeordnete Prozesse an, die dann parallel (möglicherweise auf mehreren Prozessoren) auf demselben PC laufen

Die meisten Treffer in Bezug auf "gemeinsam genutzte Variablen" und "Interprozesskommunikation" (IPC) behandeln Fälle wie diese beiden; in beiden Fällen ist jedoch ein "Elternteil" zu beobachten, auf den die "Kinder" in der Regel einen Verweis haben.

Was mich jedoch interessiert, ist die Ausführung mehrerer Aufrufe desselben Skripts, die unabhängig voneinander ausgeführt werden, und die gemeinsame Nutzung von Daten zwischen diesen (wie in Python: Gemeinsame Nutzung einer Objektinstanz bei mehreren Aufrufen eines Skripts ), in einem Singleton/Single-Instance-Modus. Diese Art von Problem wird durch die beiden oben genannten Fälle nicht wirklich gelöst - stattdessen reduziert es sich im Wesentlichen auf das Beispiel in OP (gemeinsame Nutzung von Variablen in zwei Skripten).

Wenn man dieses Problem in Perl behandelt, gibt es IPC::Shareable die es "ermöglicht, eine Variable an einen gemeinsamen Speicher zu binden", indem "eine Ganzzahl oder eine 4-stellige Zeichenkette[1] verwendet wird, die als gemeinsamer Bezeichner für Daten im gesamten Prozessraum dient". Es gibt also keine temporären Dateien und keine Netzwerkeinstellungen - was ich für meinen Anwendungsfall großartig finde; deshalb habe ich nach dem gleichen in Python gesucht.

Da jedoch akzeptierte Antwort von @Drewfer Anmerkungen: " Sie werden nicht in der Lage sein, das zu tun, was Sie wollen, ohne die Informationen irgendwo außerhalb der beiden Instanzen des Interpreters zu speichern "; oder mit anderen Worten: entweder müssen Sie ein Netzwerk/Socket-Setup verwenden - oder Sie müssen temporäre Dateien verwenden (ergo, kein gemeinsames RAM für " völlig getrennte Python-Sitzungen ").

Trotz dieser Überlegungen ist es ziemlich schwierig, funktionierende Beispiele zu finden (außer für pickle ) - auch in den Dokumenten für mmap y Multiprozessorbetrieb . Es ist mir gelungen, einige andere Beispiele zu finden - die auch einige Fallstricke beschreiben, die in den Unterlagen nicht erwähnt werden:

Dank dieser Beispiele habe ich mir ein Beispiel ausgedacht, das im Wesentlichen dasselbe tut wie die mmap Beispiel, mit Ansätzen aus dem " ein Python-Diktat synchronisieren " Beispiel - mit BaseManager (über manager.start() durch Dateipfadadresse) mit gemeinsamer Liste; sowohl Server als auch Client lesen und schreiben (unten eingefügt). Beachten Sie das:

  • multiprocessing Manager können entweder über manager.start() o server.serve_forever()
    • serve_forever() Schlösser - start() nicht
    • Es gibt eine automatische Protokollierungsfunktion in multiprocessing : Es scheint gut zu funktionieren mit start() Prozesse - scheint aber die Prozesse zu ignorieren, die serve_forever()
  • Die Adressangabe in multiprocessing kann ein IP-Pfad (Socket) oder eine temporäre Datei (möglicherweise eine Pipe?) sein; in multiprocessing docs:
    • Die meisten Beispiele verwenden multiprocessing.Manager() - ist dies nur eine Funktion ( nicht Instanziierung der Klasse), die eine SyncManager , die eine spezielle Unterklasse von BaseManager ; und Verwendungen start() - sondern nicht für IPC zwischen unabhängig laufenden Skripten; hier wird ein Dateipfad verwendet
    • Einige weitere Beispiele serve_forever() Ansatz für IPC zwischen unabhängig laufenden Skripten; hier wird die IP/Socket-Adresse verwendet
    • Wenn keine Adresse angegeben ist, wird automatisch ein temporärer Dateipfad verwendet (siehe 16.6.2.12. Protokollierung für ein Beispiel, wie man dies sehen kann)

Zusätzlich zu all den Fallstricken in der " ein Python-Diktat synchronisieren "Bei einer Liste gibt es zusätzliche Einträge. Dieser Beitrag stellt fest:

Alle Manipulationen des Diktats müssen mit Methoden und nicht mit Diktatzuweisungen erfolgen (syncdict["blast"] = 2 wird aufgrund der Art und Weise, wie die Mehrprozessoren benutzerdefinierte Objekte gemeinsam nutzen, kläglich scheitern)

Die Umgehungslösung für dict['key'] und Einstellung, ist die Verwendung des dict öffentliche Methoden get y update . Das Problem ist, dass es keine solchen öffentlichen Methoden als Alternative für list[index] für eine gemeinsame Liste müssen wir also zusätzlich registrieren __getitem__ y __setitem__ Methoden (die privat sind für list ) als exposed , was bedeutet, dass wir auch alle öffentlichen Methoden für list außerdem :/

Nun, ich denke, das waren die kritischsten Dinge; dies sind die beiden Skripte - sie können einfach in separaten Terminals ausgeführt werden (Server zuerst); Anmerkung: entwickelt auf Linux mit Python 2.7:

a.py (Server):

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 (Kunde)

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()

Eine letzte Bemerkung zu Linux /tmp/mypipe wird erstellt - hat aber 0 Bytes und die Attribute srwxr-xr-x (für einen Socket); ich schätze, das macht mich glücklich, da ich mich weder um Netzwerk-Ports noch um temporäre Dateien als solche kümmern muss :)

Andere damit zusammenhängende Fragen:

26voto

Drewfer Punkte 301

Sie werden nicht in der Lage sein, das zu tun, was Sie wollen, ohne die Informationen irgendwo außerhalb der beiden Instanzen des Interpreters zu speichern.
Wenn Sie nur einfache Variablen benötigen, können Sie einfach ein Python-Dict mit dem Pickle-Modul in Skript eins in eine Datei auslagern und es dann in Skript zwei wieder laden. Beispiel:

ein.py

import pickle

shared = {"Foo":"Bar", "Parrot":"Dead"}
fp = open("shared.pkl","w")
pickle.dump(shared, fp)

zwei.py

import pickle

fp = open("shared.pkl")
shared = pickle.load(fp)
print shared["Foo"]

4 Stimmen

Ich habe einen ähnlichen Ansatz verwendet. Anstelle von Pickle speichere ich den Variablenwert in einer Textdatei. Irgendeine Idee, was ist besser Text oder Pickle-Datei?

0 Stimmen

Für Daten, die über Ereignisströme kommen, wird es nicht funktionieren, weil ich nicht weiß, wie ich herausfinden kann, wie viele Daten bereits verarbeitet wurden.

0 Stimmen

Ich habe ein Programm, das in eine solche Datei schreiben kann und mehrere, die sie lesen können. Hat jemand einen Beweis dafür, dass diese Programme sich nicht gegenseitig stören, wenn sie versuchen, genau im selben Moment zu lesen und zu schreiben?

20voto

ruben Punkte 171
sudo apt-get install memcached python-memcache

ein.py

import memcache
shared = memcache.Client(['127.0.0.1:11211'], debug=0)
shared.set('Value', 'Hello')

zwei.py

import memcache
shared = memcache.Client(['127.0.0.1:11211'], debug=0)    
print shared.get('Value')

0 Stimmen

Ist es möglich, Ihre Lösung mit komplexen Objekten wie APScheduler-Objekten zu verwenden?

1 Stimmen

Welches Paket haben Sie für den Memcache verwendet?

1 Stimmen

@Hamed pymemcached

6voto

AmitE Punkte 854

Können Sie die relativ einfache mmap-Datei verwenden. können Sie die shared.py verwenden, um die gemeinsamen Konstanten zu speichern. Der folgende Code funktioniert in verschiedenen Python-Interpretern \ Skripten \processes

shared.py:

MMAP_SIZE = 16*1024 
MMAP_NAME = 'Global\\SHARED_MMAP_NAME'

* Das "Global" ist die Windows-Syntax für globale Namen.

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)                                                                      

two.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                                                                  

*Dieser Code wurde für Windows geschrieben, für Linux sind möglicherweise kleine Anpassungen erforderlich.

mehr Infos unter - https://docs.python.org/2/library/mmap.html

6voto

Benyamin Jafari Punkte 20585

Teilen Sie eine dynamische Variable durch Redis :

skript_eins.py

from redis import Redis
from time import sleep

cli = Redis('localhost')
shared_var = 1

while True:
   cli.set('share_place', shared_var)
   shared_var += 1
   sleep(1)

Ejecutar skript_eins in einem Terminal (einem Prozess):

$ python script_one.py

skript_zwei.py

from redis import Redis
from time import sleep

cli = Redis('localhost')

while True:
    print(int(cli.get('share_place')))
    sleep(1)

Ejecutar skript_zwei in einem anderen Terminal (einem anderen Prozess):

$ python script_two.py

Aus:

1
2
3
4
5
...

Abhängigkeiten:

$ pip install redis
$ apt-get install redis-server

0 Stimmen

Was wäre, wenn Sie die Redis-Verbindungspool zwischen zwei Kommandozeilenprozessen?

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X