0

我有两个脚本 parent.py 和 child.py parent.py 调用 child.py 作为子进程。Child.py 有一个在字典中收集某些结果的函数,我希望将该字典返回给父进程。我尝试通过将该字典从 child.py 打印到它的 STDOUT 上,以便父进程可以读取它,但这对我没有帮助,因为字典的内容正在被父进程作为单独的行中的字符串读取。

此外,正如评论中所建议的,我尝试使用 JSON 序列化字典,同时在标准输出上打印它,并使用 JSON 从父级读回它,这工作正常,但我也将许多其他信息从子级打印到其标准输出最终也被父母阅读并混淆了。

提出的另一个建议是将子级的结果写入目录中的一个文件,并让父级从该文件中读取。这也可以,但我会在 Celery 中运行 100 多个此代码的实例,因此它会导致子的其他实例覆盖同一个文件。

我的问题是,因为我们有一个连接两个进程的管道,我如何才能将我的字典直接从 child.py 写入管道并从 parent.py 读取

# parent.py

import subprocess

proc = subprocess.Popen(['python3', 'child.py'],
                        stdin=subprocess.PIPE,
                        stdout = subprocess.PIPE
                        )
proc.comunicate()
result = proc.stdout
#child.py

def child_function():
    result = {}
    result[1] = "one"
    result[2] = "two"
    print(result)
    #return result
    
if __name__ == "__main__":
    child_function()
4

3 回答 3

2

运行 Python 的子进程与运行其他东西的子进程没有任何不同。Python 不知道也不关心另一个程序也是 Python 程序;他们无法访问彼此的变量、内存、运行状态或其他内部信息。简单地想象子进程是一个整体的二进制文件。您可以与之通信的唯一方法是发送和接收字节(可以是字符串,如果您同意字符编码)和信号(这样您就可以杀死您的子进程,或引发其他可以捕获和处理的信号 - - 就像一个计时器;当计时器到期时,您只得到一位信息,而您对该位的处理取决于信号的接收者)。

“序列化”信息意味着以允许接收者反序列化的方式对其进行编码。JSON 就是一个很好的例子;您可以将由字典或列表(可能是嵌套结构)组成的结构作为文本传输,并且接收者将知道如何将该字节流映射到相同的结构中。

当发送者和接收者都运行相同的 Python 版本时,你也可以使用 pickle;pickle 是一种原生 Python 格式,它允许您传输更丰富的结构。但如果您的需求不大,我会选择 JSON。

parent.py

import subprocess
import json

# Prefer subprocess.run() over bare-bones Popen()
proc = subprocess.run(['python3', 'child.py'],
    check=True, capture_output=True, text=True)
result = json.loads(proc.stdout)

child.py

import json
import logging

def child_function():
    result = {}
    result[1] = "one"
    result[2] = "two"
    loggging.info('Some unrelated output which should not go into the JSON')
    print(json.dumps(result))
    #return result
    
if __name__ == "__main__":
    logging.basicConfig(level=logging.WARNING)
    child_function()

为避免将 JSON 与其他输出混合,请将其他输出打印到标准错误而不是标准输出(或者想办法将其嵌入到 JSON 中)。该logging模块是一种方便的方法,您可以轻松、部分或全部关闭它(上面的示例演示了关闭的日志记录,logging.basicConfig因为它只选择打印优先级WARNING或更高的消息,不包括INFO)。父母将在proc.stderr.

于 2020-10-19T10:39:34.607 回答
2

让父级为子级创建一个 FIFO(命名管道):

with os.mkfifo(mypipe) as pipe:
    proc = subprocess.Popen(['python3', 'child.py', 'mypipe'],
            stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    print(pipe.read())

现在孩子可以这样做:

pipe_path = # get from argv
with open(pipe_path, 'w') as pipe:
    pipe.write(str(result))

这使您的通信与 stdin/stdout/stderr 分开。

于 2020-10-19T10:42:55.427 回答
1

您可以通过文件获取结果。

父.py:

import tempfile
import os
import subprocess
import json


fd, temp_file_name = tempfile.mkstemp() # create temporary file
os.close(fd) # close the file
proc = subprocess.Popen(['python3', 'child.py', temp_file_name]) # pass file_name
proc.communicate()
with open(temp_file_name) as fp:
    result = json.load(fp) # get dictionary from here
os.unlink(temp_file_name) # no longer need this file

孩子.py:

import sys
import json


def child_function(temp_file_name):
    result = {}
    result[1] = "one"
    result[2] = "two"
    with open(temp_file_name, 'w') as fp:
        json.dump(result, fp)

    
if __name__ == "__main__":
    child_function(sys.argv[1]) # pass the file name argument
于 2020-10-19T11:01:39.027 回答