在单独运行的 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 文件

将参数从cmd传递到python脚本[重复]

Python - 想要在它们都运行时将数据从一个脚本传递到另一个脚本

在连续运行的 C 和 Python 应用程序之间传递数据

传递元组嵌入式 Python

运行从 PowerShell 显式获取参数的 Python 函数(无需单独传递参数)