在单独运行的 Python 脚本之间传递数据
Posted
技术标签:
【中文标题】在单独运行的 Python 脚本之间传递数据【英文标题】:Passing data between separately running Python scripts 【发布时间】:2017-10-07 06:05:44 【问题描述】:如果我正在运行一个 python 脚本(带有完整的 Tkinter GUI 和所有内容)并且我想将它收集的实时数据(内部存储在数组等中)传递给另一个 python 脚本,那么最好的方法是什么那个?
我不能简单地将脚本 A 导入脚本 B,因为它将创建脚本 A 的新实例,而不是访问已经运行的脚本 A 中的任何变量。
我能想到的唯一方法是让脚本 A 写入文件,然后脚本 B 从文件中获取数据。但是,这不太理想,因为如果脚本 B 尝试读取脚本 A 已经写入的文件,则可能会发生一些不好的事情。另外,我正在寻找两个程序之间更快的通信速度。
编辑: 以下是所要求的示例。我知道为什么这不起作用,但这是需要实现的基本前提。我的源代码很长而且很不幸是保密的,所以在这里没有帮助。总之,脚本 A 运行 Tkinter 并收集数据,而脚本 B 是 views.py 作为 Django 的一部分,但我希望这可以作为 Python 的一部分来实现。
脚本 A
import time
i = 0
def return_data():
return i
if __name__ == "__main__":
while True:
i = i + 1
print i
time.sleep(.01)
脚本 B
import time
from scriptA import return_data
if __name__ == '__main__':
while True:
print return_data() # from script A
time.sleep(1)
【问题讨论】:
您应该能够将一个模块导入另一个模块,实例化单个实例(必要时使用单例),然后为该实例分配属性/值,以便您可以根据需要从中读取辅助脚本。 如果脚本不是太长或太敏感,查看源代码会有所帮助 也许你可以使用文件套接字?这似乎是流数据的一种选择。 问题含糊不清。 “将实时数据传递给另一个脚本”可能意味着许多不同的事情。你怎么通过它?通过插座?通过一个宁静的界面?作为命令行参数?您是在启动第二个程序时传递一次数据,还是随着数据的变化不断更新数据?请出示Minimal, Complete, and Verifiable example 【参考方案1】:这将使用 TCP 主机套接字将数据传入和传出两个正在运行的脚本。 https://zeromq.org/languages/python/。所需模块 zmq:使用(pip install zmq)。 这称为客户端服务器通信。服务器将等待客户端发送请求。如果服务器没有运行,客户端也不会运行。此外,这种客户端服务器通信允许您从一个设备(客户端)向另一设备(服务器)发送请求,只要客户端和服务器在同一网络上并且您更改 localhost(服务器的 localhost 标记为与:*)到您的设备(服务器)的实际 IP(IP 帮助(进入您的设备网络设置,单击您的网络图标,找到高级或属性,查找 IP 地址。注意这可能与去谷歌不同并询问您的 IP。我正在使用 IPV6。DDOS 保护。))将客户端的 localhost IP 更改为服务器 IP。对OP的问题。您是否必须让脚本 b 始终运行,或者可以将脚本 b 作为模块导入到脚本 a 中?如果是这样,请查看如何制作 python 模块。
【讨论】:
【参考方案2】:如果您想读取和修改共享数据,在两个单独运行的脚本之间,一个好的解决方案是利用 python 多处理模块,并使用Pipe() or a Queue()(参见差异@ 987654322@)。这样,您可以同步脚本,并避免有关并发和全局变量的问题(例如如果两个脚本都想同时修改一个变量会发生什么)。
正如 Akshay Apte 在他的回答中所说,使用管道/队列的最佳部分是您可以通过它们传递 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")
编辑:刚刚发现可以使用queue.get(False)来避免接收数据时的阻塞。这样就不需要先检查队列是否为空。如果你使用管道,这是不可能的。
【讨论】:
【参考方案3】:您可以使用 pickling 模块在两个 python 程序之间传递数据。
import pickle
def storeData():
# initializing data to be stored in db
employee1 = 'key' : 'Engineer', 'name' : 'Harrison',
'age' : 21, 'pay' : 40000
employee2 = 'key' : 'LeadDeveloper', 'name' : 'Jack',
'age' : 50, 'pay' : 50000
# database
db =
db['employee1'] = employee1
db['employee2'] = employee2
# Its important to use binary mode
dbfile = open('examplePickle', 'ab')
# source, destination
pickle.dump(db, dbfile)
dbfile.close()
def loadData():
# for reading also binary mode is important
dbfile = open('examplePickle', 'rb')
db = pickle.load(dbfile)
for keys in db:
print(keys, '=>', db[keys])
dbfile.close()
【讨论】:
【参考方案4】:您可以使用multiprocessing
模块在两个模块之间实现Pipe
。然后,您可以将其中一个模块作为进程启动并使用管道与其通信。使用管道最好的部分是您还可以通过它传递 python 对象,如 dict,list。
例如: mp2.py:
from multiprocessing import Process,Queue,Pipe
from mp1 import f
if __name__ == '__main__':
parent_conn,child_conn = Pipe()
p = Process(target=f, args=(child_conn,))
p.start()
print(parent_conn.recv()) # prints "Hello"
mp1.py:
from multiprocessing import Process,Pipe
def f(child_conn):
msg = "Hello"
child_conn.send(msg)
child_conn.close()
【讨论】:
这似乎是要走的路,但是为什么以下不起作用?如果把这段代码加到mp1.py的末尾:i = 0 def g(): print i if __name__ == "__main__": while True: i = i + 1 g()
为什么运行mp2.py没有返回当前的i?
不太明白你的问题。但是如果你想调用函数g(),你需要在p = Process(target=g, args=(child_conn,))
中指定它
查看编辑中的示例,如果您将代码插入其中,mp2 将返回 0 而不是 mp1 中的 i。以上是关于在单独运行的 Python 脚本之间传递数据的主要内容,如果未能解决你的问题,请参考以下文章
Django 运行 Python 脚本并将输出传递给 javascript 文件