以下は動作しません
one.py
import shared
shared.value = 'Hello'
raw_input('A cheap way to keep process alive..')
two.py
import shared
print shared.value
次の2つのコマンドラインで実行します。
>>python one.py
>>python two.py
(2番目のものは属性エラーを受け取ります)。
これを実現する方法、つまり2つのスクリプト間で変数を共有する方法はありますか?
この問題についての私のメモをここに書き留めても大丈夫だと思います。
まず最初に、私はOPの例にとても感謝しています。それは私が始めた場所でもあります。shared
は組み込みのPythonモジュールであると思うまで、完全な例は [Tutor]モジュール間のグローバル変数?? です。
ただし、「スクリプト間で変数を共有する」(またはプロセス)を探したとき-Pythonスクリプトが他のPythonで定義された変数を使用する必要がある場合ソースファイル(ただし、必ずしも実行中のプロセスではありません)-主に他の2つの使用例に遭遇しました。
そのため、「シェア変数」と「プロセス間通信」(IPC)に関するほとんどのヒットは、これら2つのようなケースについて説明しています。ただし、どちらの場合でも、「子供」が通常参照する「親」を観察できます。
ただし、私が興味を持っているのは、同じスクリプトの複数の呼び出しを実行し、独立して実行し、それらの間でデータを共有することです(- Python:スクリプトの複数の呼び出しでオブジェクトインスタンスを共有する方法 ) 、シングルトン/シングルインスタンスモード。この種の問題は、上記の2つのケースでは実際には対処されません。代わりに、本質的にOPの例(2つのスクリプト間で変数を共有する)に減少します。
さて、Perlでこの問題を処理する場合、 IPC :: Shareable ;があります。 「変数を共有メモリに関連付けることができます」、「プロセス空間全体のデータの共通識別子として機能する整数または4文字の文字列[1]」を使用します。したがって、一時ファイルやネットワーク設定はありません。これは、自分の使用例に最適です。だから私はPythonで同じものを探していました。
ただし、 @ Drewferによる承認済みの回答 の注記:「の2つのインスタンスの外部のどこかに情報を保存せずに必要なことを実行することはできません。通訳";または言い換えると、ネットワーク/ソケットの設定を使用する必要があります-または一時ファイルを使用する必要があります(エルゴ、共有なしRAM for "完全に分離= pythonセッション ")。
さて、これらの考慮事項があっても、実用的な例(pickle
を除く)を見つけるのは少し難しいです- mmap および multiprocessing のドキュメントでも。私はなんとか他の例を見つけることができました-ドキュメントが言及していないいくつかの落とし穴も説明しています:
mmap
の使用: Sharing Python mmapを使用したプロセス間のデータ| schmichaelのブログ の2つの異なるスクリプトでの作業コードmmap
は、この一時ファイルにアクセスするための特別なインターフェイスですmultiprocessing
の使用法:作業コード:SyncManager
を共有するQueue
(manager.start()
経由)の動作例サーバー書き込み、クライアント読み取り(共有データ)BaseManager
(server.serve_forever()
経由)の動作例。サーバーの書き込み、クライアントの読み取りと書き込みmultiprocessing
の落とし穴に関する優れた説明があり、SyncManager
の実用的な例です(manager.start()
を使用) )共有dictを使用;サーバーは何もせず、クライアントは読み取りと書き込みこれらの例のおかげで、私は本質的にmmap
の例と同じように、 "synchronize a python dict "の例-BaseManager
を使用して(manager.start()
を介してファイルパスアドレスを介して)共有リストで;サーバーとクライアントの両方が読み取りと書き込み(下記に貼り付け)。
multiprocessing
マネージャーは、manager.start()
またはserver.serve_forever()
から起動できます。serve_forever()
ロック-start()
はロックしませんmultiprocessing
には自動ロギング機能があります。start()
edプロセスで正常に動作するようですが、serve_forever()
のプロセスは無視されているようですmultiprocessing
のアドレス指定は、IP(ソケット)または一時ファイル(おそらくパイプ?)パスにすることができます。 multiprocessing
docs:multiprocessing.Manager()
を使用しています。これは単なる関数(notクラスのインスタンス化)であり、SyncManager
を返します。これは、BaseManager
の特別なサブクラスです。 start()
を使用しますが、IPCの場合、notは独立して実行されたスクリプト間で使用されます。ここでは、ファイルパスが使用されます。serve_forever()
アプローチ;ここではIP /ソケットアドレスが使用されます「synchronize a python dict」投稿のすべての落とし穴に加えて、リストの場合には追加の落とし穴があります。その投稿ノート:
Dictのすべての操作は、dictの割り当てではなく、メソッドを使用して行う必要があります(syncdict ["blast"] = 2は、マルチプロセッシングがカスタムオブジェクトを共有する方法が原因で、惨めに失敗します)。
dict['key']
の取得と設定の回避策は、dict
パブリックメソッドget
およびupdate
を使用することです。問題は、list[index]
に代わるものとしてそのようなパブリックメソッドがないことです。したがって、共有リストの場合、さらに__getitem__
および__setitem__
メソッド(list
に対してプライベート)をexposed
として登録する必要があります。つまり、list
のすべてのパブリックメソッドを再登録する必要があります同様に:/
まあ、それらは最も重要なものだったと思います。これらは2つのスクリプトです。これらは別々の端末で実行することができます(サーバーが先)。 LinuxでPython 2.7を使用して開発されたメモ:
a.py
(サーバー):
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
(クライアント)
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()
最後の注釈として、Linuxでは/tmp/mypipe
が作成されますが、0バイトであり、属性srwxr-xr-x
(ソケット用)を持っています。ネットワークポートや一時ファイルなどについて心配する必要がないので、これは私を幸せにしていると思います:)
その他の関連質問:
インタプリタの2つのインスタンスの外部のどこかに情報を保存しないと、思い通りのことができなくなります。
必要な単純な変数の場合は、スクリプト1でpython dictをpickleモジュールを使用してファイルに簡単にダンプし、スクリプト2で再ロードできます。例:
one.py
import pickle
shared = {"Foo":"Bar", "Parrot":"Dead"}
fp = open("shared.pkl","w")
pickle.dump(shared, fp)
two.py
import pickle
fp = open("shared.pkl")
shared = pickle.load(fp)
print shared["Foo"]
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')
two.py
import memcache
shared = memcache.Client(['127.0.0.1:11211'], debug=0)
print shared.get('Value')
ここでやろうとしていること(Pythonモジュールを別のpythonインタプリタに共有する)に共有状態を保存する)は機能しません。
モジュール内の値は、あるモジュールによって更新されてから、別のモジュールによって読み取られますが、これは同じPythonインタープリター内にある必要があります。ここで実行しているように見えるのは、実際には一種のインタープロセスです。通信。これは、2つのプロセス間のソケット通信を介して実現できますが、ここでの作業に期待するものよりもはるかに簡単です。
比較的単純なmmapファイルを使用できます。 shared.pyを使用して、共通の定数を保存できます。次のコードは、さまざまなpythonインタプリタ\スクリプト\プロセス
shared.py:
MMAP_SIZE = 16*1024
MMAP_NAME = 'Global\\SHARED_MMAP_NAME'
*「グローバル」は、グローバル名のWindows構文です
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
*このコードはWindows用に作成されたもので、Linuxにはほとんど調整が必要ない場合があります
multiprocessing モジュールを使用することをお勧めします。コマンドラインから2つのスクリプトを実行することはできませんが、2つの別々のプロセスが互いに簡単に対話することができます。
ドキュメントの例から:
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()
変数をある種の永続ファイルに保存する必要があります。正確なニーズに応じて、これを行うモジュールがいくつかあります。
PickleおよびcPickleモジュールは、ほとんどのpythonオブジェクトをファイルに保存およびロードできます。
Shelveモジュールは、pythonオブジェクトをディクショナリのような構造で格納できます(舞台裏でピクルスを使用)。
Dbm/bsddb/dbhash/gdmモジュールは、文字列変数を辞書のような構造で格納できます。
Sqlite3モジュールは、軽量のSQLデータベースにデータを格納できます。
これらのほとんどの最大の問題は、異なるプロセス間で同期されないことです。あるプロセスが値を読み取っているときに別のプロセスがデータストアに書き込んでいると、不正なデータまたはデータの破損が発生する可能性があります。これを回避するには、独自のファイルロックメカニズムを作成するか、本格的なデータベースを使用する必要があります。
テキストファイルまたは環境変数を使用します。 2つは別々に実行されるため、実行しようとしていることを実際に実行することはできません。
この例では、最初のスクリプトが最後まで実行され、次に2番目のスクリプトが実行されます。つまり、何らかの永続的な状態が必要です。他の回答では、テキストファイルまたはPythonのpickle
モジュールの使用を提案しています。個人的に私は怠惰で、pickle
を使用できるときはテキストファイルを使用しません。独自のテキストファイル形式を解析するパーサーを作成する必要があるのはなぜですか?
pickle
の代わりに、json
モジュールを使用してJSONとして保存することもできます。 JSONはシンプルで一般的な標準であるため、データをPython以外のプログラムと共有する場合は、これが望ましい場合があります。 Pythonにjson
がない場合は、 simplejson を取得します。
ニーズがpickle
またはjson
を超える場合-実際に2つのPythonプログラムを同時に実行し、永続的な状態変数を更新するリアルタイム- SQLite データベースを使用することをお勧めします。ORMを使用してデータベースを抽象化すると、非常に簡単です。SQLiteとPythonの場合は Autumn ORM をお勧めします。
Redis
を使用して動的変数を共有する: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)
$ python script_one.py
from time import sleep
from redis import Redis
cli = Redis('localhost')
while True:
print(int(cli.get('share_place')))
sleep(1)
$ python script_two.py
でる:
1
2
3
4
5
...
$ pip install redis
$ apt-get install redis-server
変数をグローバルにすることでこの問題を解決することもできます
python first.py
class Temp:
def __init__(self):
self.first = None
global var1
var1 = Temp()
var1.first = 1
print(var1.first)
python second.py
import first as One
print(One.var1.first)
別々に実行する2つのスクリプト間で共有データの読み取りと変更を実行したい場合は、pythonマルチプロセッシングモジュールを利用して Pipe()を使用することをお勧めします。またはQueue() (違いは こちら を参照)。このようにして、スクリプトを同期し、同時実行性とグローバル変数に関する問題を回避します(両方のスクリプトが同時に変数を変更したい場合に発生する現象など)。
パイプ/キューの使用についての最もよい部分は、それらを介してpythonオブジェクトを渡すことができることです。
また、まだ渡されていない場合にデータを待機しないようにするメソッドもあります( queue.empty() および pipeConn.poll() )。
以下のQueue()の使用例をご覧ください。
# main.py
from multiprocessing import Process, Queue
from stage1 import Stage1
from stage2 import Stage2
s1= Stage1()
s2= Stage2()
# S1 to S2 communication
queueS1 = Queue() # s1.stage1() writes to queueS1
# S2 to S1 communication
queueS2 = Queue() # s2.stage2() writes to queueS2
# start s2 as another process
s2 = Process(target=s2.stage2, args=(queueS1, queueS2))
s2.daemon = True
s2.start() # Launch the stage2 process
s1.stage1(queueS1, queueS2) # start sending stuff from s1 to s2
s2.join() # wait till s2 daemon finishes
# stage1.py
import time
import random
class Stage1:
def stage1(self, queueS1, queueS2):
print("stage1")
lala = []
lis = [1, 2, 3, 4, 5]
for i in range(len(lis)):
# to avoid unnecessary waiting
if not queueS2.empty():
msg = queueS2.get() # get msg from s2
print("! ! ! stage1 RECEIVED from s2:", msg)
lala = [6, 7, 8] # now that a msg was received, further msgs will be different
time.sleep(1) # work
random.shuffle(lis)
queueS1.put(lis + lala)
queueS1.put('s1 is DONE')
# stage2.py
import time
class Stage2:
def stage2(self, queueS1, queueS2):
print("stage2")
while True:
msg = queueS1.get() # wait till there is a msg from s1
print("- - - stage2 RECEIVED from s1:", msg)
if msg == 's1 is DONE ':
break # ends loop
time.sleep(1) # work
queueS2.put("update lists")
[〜#〜] edit [〜#〜]: queue.get(False) を使用してデータ受信時のブロックを回避できることがわかりました。この方法では、キューが空かどうかを最初に確認する必要はありません。パイプを使用する場合、これは不可能です。