以下不起作用
one.py
import shared
shared.value = 'Hello'
raw_input('A cheap way to keep process alive..')
two.py
import shared
print shared.value
在两个命令行上运行:
>>python one.py
>>python two.py
(第二个得到属性错误,这是正确的。)
有没有办法实现这一点,即在两个脚本之间共享一个变量?
答案 0 :(得分:37)
希望能在这里记下关于这个问题的笔记。
首先,我很欣赏OP中的示例,因为这也是我开始的地方 - 尽管它让我觉得shared
是一些内置的Python模块,直到我找到了一个完整的例子在[Tutor] Global Variables between Modules ??。
然而,当我寻找"在脚本之间共享变量时#34; (或进程) - 除了Python脚本需要使用其他Python源文件中定义的变量(但不一定是运行进程)的情况 - 我经常偶然发现其他两个用例:
因此,大多数关于"共享变量"和#34;进程间通信" (IPC)讨论这两个案例;然而,在这两种情况下,人们都可以观察到父母"其中的孩子"通常都有参考。
然而,我感兴趣的是在单例/单实例模式下运行多个相同脚本的调用,独立运行以及在这些调用之间共享数据(如Python: how to share an object instance across multiple invocations of a script)。上述两种情况并未真正解决这类问题 - 相反,它本质上简化为OP中的示例(跨两个脚本共享变量)。
现在,在Perl中处理这个问题时,有IPC::Shareable;其中"允许您将变量绑定到共享内存",使用"整数或4字符串[1],用作跨进程空间的数据的公共标识符"。因此,没有临时文件,也没有网络设置 - 我认为这对我的用例很有用;所以我在Python中寻找相同的东西。
然而,正如accepted answer by @Drewfer注意到:" 如果不将信息存储在解释器的两个实例外部的某处,您将无法做您想做的事情&#34 ;;换句话说:要么必须使用网络/套接字设置 - 要么必须使用临时文件(ergo,没有共享RAM用于" 完全独立的python会话")
现在,即使考虑到这些因素,也很难找到工作示例(pickle
除外) - 同样在mmap和multiprocessing的文档中也是如此。我设法找到了一些其他的例子 - 这些例子也描述了文档没有提到的一些陷阱:
mmap
的使用:Sharing Python data between processes using mmap | schmichael's blog在两个不同的脚本中使用代码
mmap
只是用于访问此临时文件的特殊界面multiprocessing
的使用:工作代码:
SyncManager
(通过manager.start()
)与共享Queue
的工作示例; server(s)写入,客户端读取(共享数据)BaseManager
(通过server.serve_forever()
)的工作示例;服务器写入,客户端读写multiprocessing
陷阱有很好的解释,并且是SyncManager
(通过manager.start()
)与共享字典的一个有效例子;服务器什么都不做,客户端读写感谢这些示例,我想出了一个示例,它基本上与mmap
示例相同,其中来自" 的方法同步python dict &# 34;示例 - 使用BaseManager
(通过manager.start()
通过文件路径地址)和共享列表;服务器和客户端读写(粘贴在下面)。请注意:
multiprocessing
经理可以通过manager.start()
或server.serve_forever()
启动
serve_forever()
锁定 - start()
没有multiprocessing
中有自动日志工具:它似乎与start()
ed进程一起正常工作 - 但似乎忽略了那些serve_forever()
multiprocessing
中的地址规范可以是IP(套接字)或临时文件(可能是管道?)路径;在multiprocessing
文档中:
multiprocessing.Manager()
- 这只是一个函数( not 类实例化),它返回SyncManager
,它是BaseManager
的特殊子类;并在独立运行的脚本之间使用start()
- 但不用于IPC;这里使用了文件路径serve_forever()
用于IPC;这里使用IP /套接字地址除了" 同步一个python dict "中的所有陷阱。发布,如果是列表,还有其他的。该帖子指出:
dict的所有操作都必须使用方法而不是dict赋值(syncdict [" blast"] = 2会因多处理共享自定义对象的方式而失败)
dict['key']
获取和设置的变通方法是使用dict
公开方法get
和update
。问题是没有这样的公共方法作为list[index]
的替代方案;因此,对于共享列表,我们还必须将__getitem__
和__setitem__
方法(list
专用)注册为exposed
,这意味着我们还必须重新注册 - 注册list
的所有公开方法以及:/
嗯,我认为这些是最关键的事情;这些是两个脚本 - 它们可以在不同的终端中运行(服务器优先);使用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
(对于套接字);我想这让我感到高兴,因为我既不必担心网络端口,也不担心临时文件:)
其他相关问题:
答案 1 :(得分:17)
如果不将信息存储在解释器的两个实例外部的某个位置,您将无法执行所需的操作。
如果它只是你想要的简单变量,你可以轻松地将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"]
答案 2 :(得分:11)
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')
答案 3 :(得分:6)
你在这里尝试做什么(通过单独的python解释器在Python模块中存储共享状态)将不起作用。
模块中的值可以由一个模块更新,然后由另一个模块读取,但这必须在同一个Python解释器中。你在这里做的实际上是一种进程间通信;这可以通过两个进程之间的套接字通信来完成,但它远远不如你期望在这里工作那么简单。
答案 4 :(得分:5)
您可以使用相对简单的mmap文件。 您可以使用shared.py来存储公共常量。以下代码适用于不同的python解释器\ scripts \ processes
shared.py:
MMAP_SIZE = 16*1024
MMAP_NAME = 'Global\\SHARED_MMAP_NAME'
*&#34; Global&#34;是全局名称的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可能需要很少的调整
答案 5 :(得分:3)
您需要将变量存储在某种持久性文件中。根据您的具体需要,有几个模块可以执行此操作。
pickle和cPickle模块可以将大多数python对象保存并加载到文件中。
shelve模块可以将python对象存储在类似字典的结构中(在幕后使用pickle)。
dbm / bsddb / dbhash / gdm模块可以将字符串变量存储在类似字典的结构中。
sqlite3模块可以将数据存储在轻量级SQL数据库中。
其中大多数问题的最大问题是它们不会在不同进程之间同步 - 如果一个进程读取值而另一个进程正在写入数据存储区,那么您可能会收到不正确的数据或数据损坏。为了解决这个问题,您需要编写自己的文件锁定机制或使用完整的数据库。
答案 6 :(得分:3)
我建议你使用multiprocessing模块。您不能从命令行运行两个脚本,但是您可以让两个单独的进程轻松地相互通信。
来自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()
答案 7 :(得分:1)
使用文本文件或环境变量。由于两者分开运行,你无法真正做到你想要做的事情。
答案 8 :(得分:1)
在您的示例中,第一个脚本运行完成,然后运行第二个脚本。这意味着你需要某种持久状态。其他答案建议使用文本文件或Python的pickle
模块。就个人而言,我很懒,当我可以使用pickle
时,我不会使用文本文件;为什么要编写解析器来解析我自己的文本文件格式?
您也可以使用pickle
模块将其存储为JSON,而不是json
。如果您想将数据共享到非Python程序,这可能是更好的选择,因为JSON是一个简单而通用的标准。如果您的Python没有json
,请获取simplejson。
如果您的需求超出pickle
或json
- 假设您实际上希望同时执行两个Python程序并实时更新持久状态变量 - 我建议您使用SQLite数据库。使用ORM将数据库抽象出来,这非常简单。对于SQLite和Python,我建议Autumn ORM。
答案 9 :(得分:0)
Redis
共享动态变量:from redis import Redis
form time import sleep
cli = Redis('localhost')
shared_var = 0
while True:
cli.set('share_place', shared_var)
share_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
出局:
0
1
2
3
4
...
$ pip install redis
$ apt-get install redis-server
答案 10 :(得分:0)
如果您想在两个分别运行的脚本之间读取和修改共享数据,一个好的解决方案是利用python多处理模块并使用Pipe() or a Queue()(请参见区别{{ 3}})。这样,您就可以同步脚本并避免有关并发性和全局变量的问题(例如,如果两个脚本都想同时修改变量,会发生什么情况)。
关于使用管道/队列的最好部分是您可以通过它们传递python对象。
还有一些方法可以避免在尚未传递数据的情况下等待数据(here和queue.empty())。
请参见下面使用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")
编辑:刚刚发现您可以使用pipeConn.poll()来避免在接收数据时出现阻塞。这样,无需先检查队列是否为空。如果使用管道,则不可能。
答案 11 :(得分:-2)
您还可以通过将变量设置为全局变量来解决此问题
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)