Python 2.7 记录已打印内容

3

我正在为我的学生(我是导师)开发一个代码检查器。项目的要求是,他们编写一个使用print关键字打印特定字符串的函数。我希望能够通过存储并匹配到列表(或类似的东西)来测试他们所打印的内容。基本设置如下:

def checker():
    #run user code
    do some other things like save and check error messages etc

现在,在这个checker函数中,我希望能够跟踪打印出来的内容。在JavaScript中,我可以这样做:

var logs = [];
var hold_logger = console.log //saves the console.log so nothing gets ruined
console.log = function (x) { logs.push(x) };

现在当我运行学生的代码时,它不再在控制台打印,而是将值推送到logs中。我想在Python 2.7中实现同样的效果。


你可以使用subprocess运行它,然后你将获得stdout的文件句柄。 - Lennart Regebro
出于好奇,为什么你教学生Python 2.7而不是3.3呢? - abarnert
@abarnert 有很多不好的理由,其中最主要的是我不设定课程。 - marisbest2
@marisbest2:明白了。如果你有机会教授3.x,你可以像在JS中替换console.log函数一样替换print函数。这可能不是正确的答案,但有这个选项还是很好的。 - abarnert
3个回答

7
你可以将一个类似文件的对象分配给 sys.stdout,任何打印的东西都会被写入该对象中。
你可以使用 io.BytesIO() 对象 替换 stdout
import sys
from io import BytesIO

orig_stdout, sys.stdout = BytesIO()

io是Python 3中更新且更强大的I/O库,在Python 2中也可用;io.BytesIO()StringIO.StringIO()的更强大版本。

您可以通过调用sys.stdout.getvalue()来检查打印的内容;完成后,您可以从orig_stdout还原。

演示:

>>> import sys
>>> from io import BytesIO
>>> orig_stdout, sys.stdout = sys.stdout, BytesIO()
>>> print 'Hello world!'
>>> output = sys.stdout.getvalue()
>>> sys.stdout = orig_stdout
>>> output
'Hello world!\n'

请注意,我从先前的引用中恢复了sys.stdout。您也可以使用sys.__stdout__前提是没有其他内容替换了sys.stdout;保存对sys.stdout指向的内容的引用现在更加安全。

对于Python3,应该这样导入StringIOfrom io import StringIO - niekas
在Python 3中,您可以直接替换print()函数。 - Martijn Pieters
在我的情况下,更方便的做法是替换整个 sys.stdout 以捕获最终输出的文本。我有多个 print(<text>, end='') 语句,并在测试中检查最终结果。 - niekas
1
@niekas:自定义的print()函数仍然可以附加到共享文件对象。 :-) 为了明确起见,此问题明确标记为python-2.7。请参阅我另一个答案,以获取适用于Python 2和3的版本。 - Martijn Pieters

1
这里有一种方法 - 您可以使用自定义输出文件对象替换标准输出文件对象:
import sys

# A very basic logging class (it should really implement all the file object methods, but for your purposes, this will probably suffice.
class basicLogger:
    def __init__(self):
        self.log = []
    def write(self, msg):
        self.log.append(msg)
    def writelines(self, msgs):
        for msg in msgs:
            self.log.append(msg)

log = basicLogger()

# Replaces the default output file object with your logger
sys.stdout = log

def checker():
    #run user code
    print "hello!"
    print
    print "I am a student!"

checker()

# This replaces the original output file object.
sys.stdout = sys.__stdout__

print "Print log:"
for msg in log.log:
    print "\t", msg,

输出:

Print log:
    hello!  

    I am a student! 

0

在Python中,相当于Java中你所做的是用自定义文件对象替换sys.stdout

当然,这不仅仅会捕获他们使用print输出的内容;它还会捕获他们使用sys.stdout.write、或者根据他们的设置,可能会捕获他们运行的子进程的输出等等。但实际上没有办法避免这种情况,因为print不是可以替换的函数。(当然,如果你教他们使用3.x版本,那就是另一回事了。)


然而,最好的方法可能是根本不这样做。只需从 Python 外部捕获 stdout 即可。例如:

for student in student??.py:
    python ${student} > ${student}.out

或者,如果你坚持的话,可以在Python中使用subprocess完成同样的事情:

output = {student: subprocess.check_output('{}.py'.format(student)) 
          for student in students}

(你可能想把它包装在某个捕获和存储异常的东西中,但这应该足以让你有一个想法。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接