如何在Python中实现与子流程的连续交互式对话?

如何在Python中实现与子流程的连续交互式对话?,python,c,algorithm,subprocess,genetic,Python,C,Algorithm,Subprocess,Genetic,也许持续的互动不是正确的说法。我想知道是否有人能帮助我理解从Python程序中将程序作为子进程调用的基本知识?我一直在黑客攻击,但我不断遇到令人沮丧的错误。我用简单的例子最能理解。我的桌面上保存了一个名为square.py的程序,该程序使用以下代码: i=0 while i<10: x=int(raw_input('Enter x-dimension: ')) x1 = x*x print str(x1) i=i+1 i=0 而i“持续交互”与子流程模块冲

也许持续的互动不是正确的说法。我想知道是否有人能帮助我理解从Python程序中将程序作为子进程调用的基本知识?我一直在黑客攻击,但我不断遇到令人沮丧的错误。我用简单的例子最能理解。我的桌面上保存了一个名为square.py的程序,该程序使用以下代码:

i=0
while i<10:
    x=int(raw_input('Enter x-dimension: '))
    x1 = x*x
    print str(x1)
    i=i+1
i=0
而i“持续交互”与
子流程
模块冲突严重,后者使用操作系统级管道(在类Unix系统上;在Windows上它必然会做一些不同的事情)。但是,要像用户在ssh pty连接上那样与进程交互,必须创建pty会话。许多程序在与管道通话或通过管道通话时都认为它们不是交互式的

这是将古老的donlibes翻译成Python。它就是针对这种想法

该系统似乎还具备实现所需结果的必要部件


(我自己都没有使用过。)

设计用于与人交互的程序与设计用于与其他程序交互的程序不同。您的
square.py
脚本更接近前一个类别。可能的问题:

  • 提示在一行中结束,它迫使父脚本一次读取一个字节,而不是完整行或已知块,以避免阻塞
  • 孩子的答案没有被明确地刷新。这意味着,例如,当通过
    子流程运行时,没有
    pty
以下是如何使用当前形式的
子流程
模块与之交互:

#!/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
这里有同样的东西,但使用(用于比较):

这两个脚本都是为了从同一个源代码支持Python 2和Python 3而编写的

注意:基于
子流程
的脚本中有
-u
参数,即使在非交互模式下,也可以在行可用时立即读取行<基于code>pexpect
的脚本在没有此类开关的情况下工作<基于代码>stdio的程序可以使用

您可以看到,即使是最简单的子脚本(
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
它可以在“一次完成”模式下从基于
子流程的模块中使用:

或一次一个数字:

#!/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()) # e.g., {"result": [0, 0, 7]}
# ...
p.communicate() # close pipes, wait for child to finish

也许持续互动不是正确的说法。我不需要在C代码做它的事情时打扰它,我只需要给它一个值,让它做它的魔术,接受它的输出,然后重新做。由于可能需要10000次试验才能获得理想的结果,所以我需要继续使用遗传算法,直到我决定退出为止。你知道我问的“不那么难”的例子吗?谢谢一般来说,您仍然需要打开pty并使用它与底层流程“对话”的东西,这意味着类似于这些包的东西。当标准输出是管道时,C库将标准输出设置为完全(而不是行)缓冲,而且大多数C程序没有足够认真地调用
fflush
来处理管道。@torek:因此,在这种情况下可以避免块缓冲和消息边界问题,并且可以使用
子进程
模块。@J.F.Sebastian:Aha,这确实改变了情况。不过,如果您对C程序有那么多的控制权,也许可以对它进行修改,以便在独立的程序调用中存储它的状态,然后从Python包装器运行它就更容易了。:-)如果我将C代码的输出存储在一个.csv文件中,我可以将其读入python程序。我想我们可以设置它,这样我可以将用户反馈保存在一个单独的文件中,并让C程序读取反馈。这只留下了Python程序如何知道何时有新的C输出可用,以及C代码如何知道有新的用户反馈值可用的问题。感谢您的帮助@J.F.Sebastian和@torek!
#!/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)
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()) # e.g., {"result": [0, 0, 7]}
# ...
p.communicate() # close pipes, wait for child to finish