以下不起作用
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
(第二个得到属性错误,这是正确的).
有没有办法实现这一点,即在两个脚本之间共享一个变量?
希望在这里记下关于这个问题的笔记是可以的.
首先,我很欣赏OP中的示例,因为这也是我开始的地方 - 尽管它让我想到了shared
一些内置的Python模块,直到我在模块之间的[Tutor] Global Variables中找到了一个完整的例子?? .
但是,当我寻找"在脚本之间共享变量"(或进程)时 - 除了Python脚本需要使用在其他Python源文件中定义的变量(但不一定是运行进程)的情况 - 我经常偶然发现其他两个用例:
脚本将自身分成多个子进程,然后在同一台PC上并行运行(可能在多个处理器上)
脚本会生成多个其他子进程,然后在同一台PC上并行运行(可能在多个处理器上)
因此,大多数关于"共享变量"和"进程间通信"(IPC)的命中讨论了这两个案例; 然而,在这两种情况下,人们都可以观察到"父母","孩子"通常会有一个参考.
然而,我感兴趣的是运行多个相同脚本的调用,独立运行,以及在单例/单个实例中在Python之间共享数据(如在Python中如何共享对象实例的多个调用)模式.上述两种情况并未真正解决这类问题 - 相反,它本质上简化为OP中的示例(跨两个脚本共享变量).
现在,在Perl中处理这个问题时,有IPC :: Shareable ; "允许您将变量绑定到共享内存",使用"整数或4个字符串[1],作为跨进程空间的数据的公共标识符".因此,没有临时文件,也没有网络设置 - 我认为这对我的用例很有用; 所以我在Python中寻找相同的东西.
然而,正如@Drewfer所接受的回答所指出的那样:" 如果不将信息存储在解释器的两个实例之外的某个地方,你将无法做你想做的事情 "; 换句话说:要么必须使用网络/套接字设置 - 要么必须使用临时文件(ergo,没有共享RAM用于" 完全独立的python会话 ").
现在,即使考虑到这些因素,也很难找到工作示例(除了pickle
) - 在mmap和多处理的文档中也是如此.我设法找到了一些其他的例子 - 这些例子也描述了文档没有提到的一些陷阱:
用法mmap
:使用mmap |在两个不同的脚本中共享Python数据的工作代码 schmichael的博客
演示两个脚本如何更改共享值
请注意,此处创建临时文件作为已保存数据的存储 - mmap
只是用于访问此临时文件的特殊接口
用法multiprocessing
:工作代码:
多处理下的Python多处理RemoteManager.Process - SyncManager
(via manager.start()
)with shared的工作示例Queue
; 服务器写入,客户端读取(共享数据)
多处理模块和pyro的比较?- 使用共享自定义类的BaseManager
(via server.serve_forever()
)工作示例; 服务器写入,客户端读取和写入
如何将python dict与多处理同步 - 这个答案对multiprocessing
陷阱有很好的解释,并且是SyncManager
(通过manager.start()
)共享字典的一个工作示例; 服务器什么都不做,客户端读写
感谢这些例子,我想出了一个例子,它与示例基本相同mmap
,使用" 同步python dict "示例的方法 - 使用BaseManager
(manager.start()
通过文件路径地址)和共享列表; 服务器和客户端读写(粘贴在下面).注意:
multiprocessing
经理可以通过manager.start()
或启动server.serve_forever()
serve_forever()
锁 - start()
没有
有自动记录功能multiprocessing
:它似乎可以正常使用start()
ed进程 - 但似乎忽略了那些serve_forever()
地址规范multiprocessing
可以是IP(套接字)或临时文件(可能是管道?)路径; 在multiprocessing
文档中:
大多数示例使用multiprocessing.Manager()
- 这只是一个返回a 的函数(不是类实例化)SyncManager
,它是一个特殊的子类BaseManager
; 和使用start()
- 但不是独立运行的脚本之间的IPC; 这里使用了文件路径
serve_forever()
在独立运行的脚本之间,很少有其他示例用于IPC; 这里使用IP /套接字地址
如果未指定地址,则会自动使用临时文件路径(请参阅16.6.2.12.记录有关如何查看此内容的示例)
除了" 同步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# 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
(对于套接字); 我想这让我感到高兴,因为我既不必担心网络端口,也不担心临时文件:)
其他相关问题:
Python:可以在两个独立的进程之间共享内存数据(非常好的解释)
高效的Python到Python IPC
Python:将变量发送到另一个脚本
如果不将信息存储在解释器的两个实例外部的某个位置,您将无法执行所需的操作.
如果它只是你想要的简单变量,你可以轻松地将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解释器中.你在这里做的实际上是一种进程间通信; 这可以通过两个进程之间的套接字通信来完成,但它远远不如你期望在这里工作那么简单.
您可以使用相对简单的mmap文件。您可以使用shared.py存储公共常量。以下代码将在不同的python解释器\脚本\ processes中工作
shared.py:
MMAP_SIZE = 16*1024 MMAP_NAME = 'Global\\SHARED_MMAP_NAME'
*“全局”是Windows全局名称的语法
一个.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可能需要一些调整
更多信息-https: //docs.python.org/2/library/mmap.html