问题描述
我有两个脚本parent.py和child.py parent.py将child.py称为子进程。 Child.py具有在字典中收集某些结果的功能,我希望将该字典返回给父进程。我已经尝试过将字典从child.py打印到其STDOUT上,以便父进程可以读取它,但是那对我没有帮助,因为字典的内容被父文件在单独的行上读取为字符串。
此外,正如注释中所建议的那样,我在将stdout打印到字典时尝试使用JSON序列化字典,并使用JSON从父级读回它,效果很好,但是我还从子进程中打印了许多其他信息到最终被父级读取并混合在一起的标准输出。
提出的另一个建议是,将子项的结果写入目录中的文件,并使父项从该文件中读取。那也可以,但是我将在Celery中运行100个该代码实例,因此它将导致该子实例的其他实例覆盖同一文件。
我的问题是,因为我们有一个连接两个进程的PIPE,我如何才能直接将字典从child.py直接写入PIPE并从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()
解决方法
运行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
中获得这些消息。
让父级为子级创建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分开。
,您可以通过文件获取结果。
parent.py:
return Object.keys(obj).map(item => ( {name: item,...obj[item]} ));
child.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',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