38

以下は動作しません

one.py

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

2.py

import shared
print shared.value

次のように 2 つのコマンド ラインで実行します。

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

(2番目のものは属性エラーを取得します。当然のことです)。

これを達成する方法、つまり 2 つのスクリプト間で変数を共有する方法はありますか?

4

13 に答える 13

57

この問題に関する私のメモをここに書き留めてもよろしいかと思います。

まず第一に、私はOPの例にとても感謝しています。なぜなら、それは私が始めた場所でもあるからです. [Tutor]モジュール間のグローバル変数sharedで完全な例を見つけるまで、組み込みのPythonモジュールだと思いましたが?? .

ただし、「スクリプト間で変数を共有する」(またはプロセス)を探したとき-Pythonスクリプトが他のPythonソースファイルで定義された変数を使用する必要がある場合(ただし、必ずしも実行中のプロセスであるとは限らない)の場合を除いて-私は主に2つの他のユースケースに出くわしました:

  • スクリプトは複数の子プロセスに分岐し、同じ PC 上で並行して (場合によっては複数のプロセッサで) 実行されます。
  • スクリプトは他の複数の子プロセスを生成し、それらは同じ PC 上で (おそらく複数のプロセッサで) 並列に実行されます。

そのため、「共有変数」と「プロセス間通信」(IPC) に関するほとんどのヒットは、これら 2 つのようなケースについて議論しています。ただし、どちらの場合でも、通常は「子」が参照する「親」を観察できます。

ただし、私が興味を持っているのは、同じスクリプトの複数の呼び出しを実行し、独立して実行し、それらの間でデータを共有することです ( Python のように: スクリプトの複数の呼び出しでオブジェクト インスタンスを共有する方法)、シングルトン/単一インスタンスでモード。この種の問題は、上記の 2 つのケースでは実際には対処されません。代わりに、本質的に OP の例 (2 つのスクリプト間で変数を共有する) に縮小されます。

さて、Perl でこの問題を扱う場合、IPC::Shareableがあります。これにより、「プロセス空間全体のデータの共通識別子として機能する整数または4文字の文字列[1]」を使用して、「変数を共有メモリに関連付けることができます」。したがって、一時ファイルもネットワーク設定もありません。これは私のユースケースに最適です。だから私はPythonで同じものを探していました。

ただし、@Drewfer による回答が受け入れられているように、「インタープリターの 2 つのインスタンスの外部に情報を保存しなければ、やりたいことを実行することはできません」。つまり、ネットワーク/ソケットのセットアップを使用する必要があります-または一時ファイルを使用する必要があります(したがって、「完全に個別のpythonセッション」用の共有RAMはありません)。

さて、これらの考慮事項があっても、実際の例を見つけるのはちょっと難しいです (を除く) - mmapおよびmultiprocessingpickleのドキュメントでも。ドキュメントに記載されていないいくつかの落とし穴についても説明している他の例をいくつか見つけることができました。

これらの例のおかげで、「 python dict を同期するmmap」例からのアプローチで、基本的に例と同じことを行う例を思いつきました- (ファイルパスアドレスを介して)共有リストを使用します。サーバーとクライアントの両方が読み取りと書き込みを行います (以下に貼り付けます)。ご了承ください:BaseManagermanager.start()

  • multiprocessingmanager.start()manager は、またはのいずれかで開始できます。server.serve_forever()
    • serve_forever()ロック -start()しません
    • には自動ロギング機能がありますmultiprocessing: start()ed プロセスでは問題なく動作するようですが、そのようなプロセスは無視しているようです。serve_forever()
  • のアドレス指定は、multiprocessingIP (ソケット) または一時ファイル (おそらくパイプ?) パスです。multiprocessingドキュメント で:
    • ほとんどの例で使用- これは、 ;の特別なサブクラスであるを返すmultiprocessing.Manager()単なる関数 (クラスのインスタンス化ではない) です。and uses - ただし、独立して実行されたスクリプト間の IPC には使用されません。ここではファイルパスが使用されますSyncManagerBaseManagerstart()
    • serve_forever()独立して実行されたスクリプト間の IPC のアプローチの例は他にほとんどありません。ここではIP /ソケットアドレスが使用されています
    • アドレスが指定されていない場合、一時ファイルのパスが自動的に使用されます (これを確認する方法の例については、16.6.2.12. ログを参照してください)。

「 python dict を同期する」投稿のすべての落とし穴に加えて、リストの場合には追加の落とし穴があります。その投稿は次のように述べています。

dict のすべての操作は、dict 割り当てではなくメソッドで行う必要があります (マルチプロセッシングがカスタム オブジェクトを共有する方法のため、syncdict["blast"] = 2 は惨めに失敗します)。

dict['key']取得と設定の回避策は、dictパブリック メソッドgetと を使用することですupdatelist[index]問題は、 ;の代替となるパブリック メソッドがないことです。したがって、共有リストの場合、さらに__getitem____setitem__メソッド ( のプライベートlist) を としてexposed登録する必要があります。つまり、 のすべてのパブリック メソッドも再登録する必要listがあります。:/

ええと、それらが最も重要なことだったと思います。これらは 2 つのスクリプトです。これらは別々のターミナルで実行できます (サーバーが最初)。Python 2.7 を使用して Linux で開発されたノート:

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(ソケットの) 属性があります。ネットワークポートや一時ファイル自体について心配する必要がないので、これは私を幸せにすると思います:)

その他の関連する質問:

于 2013-02-05T04:45:07.653 に答える
25

インタープリターの 2 つのインスタンスの外部のどこかに情報を保存しないと、やりたいことができません。
単純な変数が必要な場合は、スクリプト 1 で pickle モジュールを使用して Python dict をファイルに簡単にダンプし、スクリプト 2 で再ロードできます。例:

one.py

import pickle

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

2.py

import pickle

fp = open("shared.pkl")
shared = pickle.load(fp)
print shared["Foo"]
于 2009-12-01T22:05:37.730 に答える
17
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')

2.py

import memcache
shared = memcache.Client(['127.0.0.1:11211'], debug=0)    
print shared.get('Value')
于 2010-10-09T22:54:13.790 に答える
6

ここでやろうとしていること (共有状態を別の Python インタープリターを介して Python モジュールに保存する) は機能しません。

モジュール内の値は、あるモジュールによって更新され、別のモジュールによって読み取られる可能性がありますが、これは同じ Python インタープリター内にある必要があります。ここで行っているように見えるのは、実際には一種のプロセス間通信です。これは、2 つのプロセス間のソケット通信を介して実現できますが、ここで期待する作業よりもはるかに簡単ではありません。

于 2009-12-01T21:48:48.880 に答える
6

相対単純 mmap ファイルを使用できます。shared.py を使用して、共通の定数を格納できます。次のコードは、さまざまな python インタープリター\スクリプト\プロセス間で機能します

共有.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)                                                                      

2.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 では少し調整が必要な場合があります

詳細 - https://docs.python.org/2/library/mmap.html

于 2015-01-27T10:08:00.007 に答える
4

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()
于 2009-12-02T04:01:06.383 に答える
3

変数をある種の永続ファイルに保存する必要があります。正確なニーズに応じて、これを行うためのいくつかのモジュールがあります。

pickleおよびcPickleモジュールは、ほとんどのPythonオブジェクトをファイルに保存およびロードできます。

shelveモジュールは、Pythonオブジェクトを辞書のような構造で格納できます(舞台裏でpickleを使用)。

dbm / bsddb / dbhash / gdmモジュールは、文字列変数を辞書のような構造で格納できます。

sqlite3モジュールは、軽量のSQLデータベースにデータを保存できます。

これらのほとんどの最大の問題は、異なるプロセス間で同期されていないことです。あるプロセスがデータストアに書き込んでいるときに別のプロセスが値を読み取ると、誤ったデータやデータの破損が発生する可能性があります。これを回避するには、独自のファイルロックメカニズムを作成するか、本格的なデータベースを使用する必要があります。

于 2009-12-01T21:50:11.840 に答える
1

テキストファイルまたは環境変数を使用します。2つは別々に実行されるため、実行しようとしていることを実際に実行することはできません。

于 2009-12-01T21:51:02.683 に答える
1

あなたの例では、最初のスクリプトが最後まで実行され、次に 2 番目のスクリプトが実行されます。つまり、ある種の永続的な状態が必要です。他の回答では、テキスト ファイルまたは Python のpickleモジュールを使用することが提案されています。個人的に私は怠け者で、使用できる場合はテキスト ファイルを使用しませんpickle。独自のテキスト ファイル形式を解析するパーサーを作成する必要があるのはなぜですか?

代わりに、モジュールを使用して JSON として保存するpickleこともできます。jsonJSON はシンプルで一般的な標準であるため、Python 以外のプログラムとデータを共有する場合は、これが望ましい場合があります。Python に がない場合はjsonsimplejsonを取得します。

それ以上のニーズがある場合、pickleまたはjson実際に 2 つの Python プログラムを同時に実行し、永続的な状態変数をリアルタイムで更新する必要がある場合は、SQLiteデータベースを使用することをお勧めします。ORM を使用してデータベースを抽象化します。これは非常に簡単です。SQLite と Python については、秋の ORMをお勧めします。

于 2009-12-01T23:59:10.913 に答える
-1

Pythonの基本関数fromimport関数を使用して、変数を にインポートできますtwo.py。例えば:

from filename import variable

これにより、ファイルから変数がインポートされます。(もちろん、 に置き換えfilenameて、共有したい変数にone.py置き換えてください。)variabletwo.py

于 2021-11-02T18:09:13.310 に答える