如何在Python中使用子进程实现连续交互式对话?

也许连续互动不是正确的短语。 我想知道是否有人可以帮助我理解从Python程序调用程序作为子进程的基础知识? 我一直在乱砍,但我一直遇到令人沮丧的错误。 我最好用简单的例子。 我有一个名为square.py的程序保存到我的桌面,使用以下代码:

i=0 while i<10: x=int(raw_input('Enter x-dimension: ')) x1 = x*x print str(x1) i=i+1 

有人可以用简单的语言向我解释如何在IDLE中调用此程序并与它保持持续的交互式对话(保持打开并在后台运行),直到它自行终止?

最后,我需要利用这些知识从Python GUI(使用tkinter)调用用C编写的遗传算法程序。 遗传算法输出一组值,用户使用这些值做某事并向用户提供关于这些值的效用的反馈。 用户反馈的forms为0-100。 当遗传算法接收到输入时,它会发挥其魔力并输出另一个数字数组,这有望提供更好的效用。 因此,我想围绕一个可怕的C程序包装Python GUI,为C程序提供反馈值并接收一组数字。

我希望我解释了我想做的事情; 如果有人可以帮助我使用子进程调用square.py,传递一个值并返回其输出我会很高兴。 干杯!

旨在与人交互的程序与旨在与其他程序交互的程序不同。 你的square.py脚本更接近前一类。 可能的问题:

  • 提示在一行中间结束,该行强制父脚本一次读取一个字节而不是完整行或已知块以避免阻塞
  • 孩子的答案没有明确刷新。 这意味着可以在非交互模式下启用块缓冲,例如,当它通过没有pty subprocess pty运行时

以下是使用当前forms的subprocess模块与它进行交互的方法:

 #!/usr/bin/env python from __future__ import print_function import sys from itertools import cycle from subprocess import Popen, PIPE from textwrap import dedent # start child process p = Popen([sys.executable or 'python', '-u', '-c', dedent(""" for i in range(10): x = int(input('Enter x-dimension: ')) print(x*x) """)], stdin=PIPE, stdout=PIPE, universal_newlines=True, bufsize=1) for n in cycle([3, 1, 4, 15, 926]): # infinite loop while p.poll() is None: # while the subprocess is running # send input to the child print(n, file=p.stdin) # read & parse answer data = p.stdout.readline().rpartition(' ')[2] if not data: # EOF answer = None break # exit inner loop answer = int(data) if answer == 1: # show example when input depends on output n += 1 else: # done with given `n` break # exit inner loop else: # subprocess ended break # exit outer loop if answer is not None: print("Input %3d Output %6d" % (n, answer)) p.communicate() # close pipes, wait for the child to terminate 

这是相同的事情,但使用pexpect (用于比较):

 #!/usr/bin/env python import sys from itertools import cycle from textwrap import dedent import pexpect child = pexpect.spawnu(sys.executable or 'python', ['-c', dedent(""" for i in range(10): x = int(input('Enter x-dimension: ')) print(x*x) """)]) for n in cycle([3, 1, 4, 15, 926]): while True: i = child.expect([pexpect.EOF, u'x-dimension:']) if i == 0: # EOF answer = None child.close() sys.exit() elif i == 1: # child waits for input child.sendline(str(n)) child.expect(u'\\n\\d+\\s') answer = int(child.after) if answer == 1: n += 1 else: break else: assert 0 else: # child terminated break if answer is not None: print("Input %3d Output %6d" % (n, answer)) 

编写这两个脚本是为了支持来自同一源的Python 2和Python 3。

注意:基于subprocess的脚本中有-u参数,即使在非交互模式下,它们也可以在行可用时立即读取。 基于pexpect的脚本在没有这种切换的情况下工作。 基于stdio的程序可以使用stdbufunbuffer实用程序或提供pty来无缓冲/进行行缓冲。

你可以看到,即使是最简单的子脚本( square.py )也需要克服几个问题才能完成工作。

当子程序期望从另一个程序运行同时保持人类可读(可调试)时,一切都更简单。 在这种情况下, square.py可能如下所示:

 #!/usr/bin/env python import sys import time for line in iter(sys.stdin.readline, ''): # get line as soon as it is available print(int(line)**2) # find square sys.stdout.flush() # make the answer available immediately time.sleep(.5) # a delay to show that the answer is available immediately 

它可以在“一次性”模式下从基于subprocess的模块中使用:

 import sys from subprocess import Popen, PIPE L = [2, 7, 1] # numbers to be squared p = Popen([sys.executable or 'python', 'square.py'], stdin=PIPE, stdout=PIPE, universal_newlines=True, bufsize=-1) answers = map(int, p.communicate("\n".join(map(str, L)))[0].splitlines()) 

或者一次一个数字:

 #!/usr/bin/env python import sys from subprocess import Popen, PIPE answers = [] p = Popen([sys.executable or 'python', 'square.py'], stdin=PIPE, stdout=PIPE, bufsize=1) for c in [b'2', b'7', b'1']: p.stdin.write(c + b'\n') p.stdin.flush() answers.append(int(p.stdout.readline())) print(answers) p.communicate() # close pipes, wait for child to finish print(answers) 

从C程序中获取数组; 你可以json模块:

 import json from subprocess import Popen, PIPE p = Popen(['./c-program', 'other', 'args'], stdin=PIPE, stdout=PIPE, bufsize=1) p.stdin.write(json.dumps({'parameter': 8}).encode() + b'\n') # send input p.stdin.flush() result = json.loads(p.stdout.readline().decode()) # eg, {"result": [0, 0, 7]} # ... p.communicate() # close pipes, wait for child to finish 

“持续交互”与使用操作系统级别管道的subprocess模块(在类似Unix的系统上;它必然会在Windows上做一些不同的事情)发生严重冲突。 但是,要按照用户的方式与进程交互,例如ssh pty连接,您必须创建一个pty会话。 许多程序在与管道或通过管道交谈时都认为它们不是交互式的。

pexpect模块是旧的Don Libes 对 Python的期望 。 它针对这种想法。

sh模块似乎也具有实现所需结果的必要部分。

(我自己也没用过任何一个。)