在IPython并行进程中向stdout打印

8 投票
1 回答
6012 浏览
提问于 2025-04-17 18:23

我刚接触IPython,想在运行IPython并行集群函数时,把中间结果打印到标准输出(stdout)。我知道因为有多个进程,这可能会让输出变得混乱,但没关系——这只是为了测试和调试,而且我运行的进程比较长,所以这种冲突发生的可能性不大。我查了IPython的文档,但找不到一个示例,说明并行化的函数是如何打印输出的。

简单来说,我想找到一种方法,把子进程的打印输出重定向到主标准输出,也就是IPython中的等价方式。

subprocess.Popen( ... , stdout=...)

在进程内部打印是行不通的:

rc = Client()
dview = rc()
def ff(x):
    print(x)
    return x**2
sync = dview.map_sync(ff,[1,2,3,4])
print('sync res=%s'%repr(sync))
async = dview.map_async(ff,[1,2,3,4])
print('async res=%s'%repr(async))
print(async.display_outputs())

返回的结果是

sync res=[1, 4, 9, 16]
async res=[1, 4, 9, 16]

所以计算是正确执行的,但在函数ff中的打印语句从来没有被打印出来,即使所有进程都已经返回了。我哪里做错了?我该如何让“打印”工作?

1 个回答

9

其实这和 subprocess.Popen( ... , stdout=PIPE) 更像,而不是你想的那样。就像 Popen 对象有一个 stdout 属性,你可以通过这个属性查看子进程的标准输出一样,AsyncResult 也有一个 stdout 属性,它包含了从引擎捕获的标准输出。

不过有一点不同的是,AsyncResult.stdout 是一个字符串列表,列表中的每一项都是一个引擎的标准输出,都是字符串形式。

那么,首先:

rc = parallel.Client()
dview = rc[:]
def ff(x):
    print(x)
    return x**2
sync = dview.map_sync(ff,[1,2,3,4])
print('sync res=%r' % sync)
async = dview.map_async(ff,[1,2,3,4])
print('async res=%r' % async)
async.get()

会得到

sync res=[1, 4, 9, 16]
async res=<AsyncMapResult: ff>

我们可以看到 AsyncResult.stdout 的字符串列表:

print(async.stdout)
['1\n2\n', '3\n4\n']

我们可以看到异步结果的标准输出:

print('async output:')
async.display_outputs()

打印出来的是:

async output:
[stdout:0] 
1
2
[stdout:1] 
3
4

还有这里有一个示例笔记本,展示了所有这些内容。

根据你的问题,有几点需要注意:

  1. 你必须等到 AsyncResult 完成后,输出才会准备好(使用 async.get())。
  2. display_outputs() 不会返回任何东西——它实际上是自己负责打印和显示的,所以 print(async.display_outputs()) 是没有意义的。

撰写回答