在Python中显示exec的当前输出

0 投票
2 回答
1146 浏览
提问于 2025-04-17 14:48

我运行的代码如下(是从其他话题复制过来的,并加了sleep):

import sys
import StringIO
import contextlib

@contextlib.contextmanager
def stdoutIO(stdout=None):
    old = sys.stdout
    if stdout is None:
        stdout = StringIO.StringIO()
    sys.stdout = stdout
    yield stdout
    sys.stdout = old

code = """
import time
i = [0,1,2]
for j in i :
    print j
    time.sleep(5)
"""
with stdoutIO() as s:
    exec code

print "out:", s.getvalue()

当我运行这段代码时,我得等15秒,程序才结束才能看到输出。可是我想要在每次循环的每5秒就能看到print语句的输出,而不是等15秒才能看到全部的输出。

这有可能吗?能不能实时看到执行的当前输出呢?

2 个回答

1

这个问题和exec没有关系。问题在于你把标准输出(stdout)重定向到了一个StringIO对象。所有的打印语句都在往这个StringIO里添加内容,而不是直接打印到屏幕上。所以你看不到任何输出。唯一一行能在屏幕上打印内容的是

print "out:", s.getvalue()

这行代码是在15秒的睡眠之后执行的。


with stdoutIO() as s:
    ...

这段代码把stdout重定向到了StringIO.StringIO。只有在with上下文结束后,stdout才会被重定向回原来的sys.stdout,这样才能在终端上打印出来。


所以,如果你想在执行时看到print语句的输出,只需去掉stdeoutIO的上下文管理器:

import sys

code = """
import time
i = [0,1,2]
for j in i :
    print j
    time.sleep(5)
"""

exec code
1

在你发的代码中,使用的上下文管理器在执行 with 的时候只会运行一次。所以,代码必须先执行完(总共15秒),然后所有的输出结果都会存储在 StringIO 这个对象里。因此,使用这样的代码,你 无法 实时获取输出。

如果你想要实时输出,你需要提供一个自己的类似 file 的类,而不是使用 StringIO,并且重写 write 方法。比如(这个例子只是个简单粗糙的代码):

import sys
import StringIO
import contextlib

class Proxy(object):
    def __init__(self,stdout,stringio):
        self._stdout = stdout
        self._stringio = stringio
    def __getattr__(self,name):
        if name in ('_stdout','_stringio','write'):
            object.__getattribute__(self,name)
        else:
            return getattr(self._stringio,name)
    def write(self,data):
         self._stdout.write(data)
         self._stringio.write(data)

@contextlib.contextmanager
def stdoutIO(stdout=None):
    old = sys.stdout
    if stdout is None:
        stdout = StringIO.StringIO()
    sys.stdout = Proxy(sys.stdout,stdout)
    yield sys.stdout
    sys.stdout = old

code = """
import time
i = [0,1,2]
for j in i :
    print j
    time.sleep(5)
"""

with stdoutIO() as s:
    exec code

print "out:", s.getvalue()

这个代码的输出是:

0
1
2
out: 0
1
2

前面三行每隔5秒打印一次,最后三行则是在结束时一次性打印出来。

撰写回答