4

我正在制作一个简单的测试函数,它通过从文件中读取要评估的表达式和预期结果来断言我正在开发的解释器的输出是正确的,就像 python 的 doctest。这是用于方案,因此输入文件的示例是

> 42
42

> (+ 1 2 3)
6

我对可以解析此类文件的函数的第一次尝试如下所示,它似乎按预期工作:

def run_test(filename):
    interp = Interpreter()
    response_next = False
    num_tests = 0
    with open(filename) as f:
        for line in f:
            if response_next:
                assert response == line.rstrip('\n')
                response_next = False
            elif line.startswith('> '):
                num_tests += 1
                response = interp.eval(line[2:])
                response = str(response) if response else ''
                response_next = True
    print "{:20} Ran {} tests successfully".format(os.path.basename(filename),
                                                    num_tests)

我想通过删除response_next标志来稍微改进它,因为我不喜欢这样的标志,而是在elif块中的下一行读取next(f). 关于我在 freenode 的 IRC 中提出的问题,我有一个无关紧要的小问题。我得到了我想要的帮助,但我也得到了使用的建议f.readlines(),然后在结果列表上使用索引。(我还被告知我可以将groupby()initertools用于成对线,但我稍后会研究这种方法。)

现在到这个问题,我很好奇为什么这种方法会更好,但是我的互联网连接在火车上是不稳定的,我无法问,所以我会在这里问它。readlines()为什么阅读所有内容而不是在动态阅读时解析每一行会更好?

我真的很想知道,因为我的感觉是相反的,我认为一次解析一行似乎更干净,这样一切都可以一口气完成。我通常避免在 Python 的数组中使用索引,而是更喜欢使用迭代器和生成器。如果这是一个主观意见,也许不可能回答和猜测这个人在想什么,但如果有一些一般性的建议,我很乐意听到。

4

3 回答 3

1

迭代地处理输入肯定比一次读取整个输入更符合 Pythonic;例如,如果输入是控制台,这将起作用。

支持读取整个数组和索引的一个论点是,与循环next(f)结合使用时可能会不清楚;for选项可以for用 a 替换循环while True或完整记录您在循环中调用next的内容f

try:
    while True:
        test = next(f)
        response = next(f)
except StopIteration:
    pass

正如 Jonas 建议的那样,您可以通过压缩输入来完成此操作(如果您确定输入将始终包含行 test/response/test/response 等):

for test, response in zip(f, f):               # Python 3
for test, response in itertools.izip(f, f):    # Python 2
于 2012-07-11T12:58:28.060 回答
0
from itertools import ifilter,imap

def run_test(filename):
    interp = Interpreter()
    num_tests, num_passed, last_result = 0, 0, None
    with open(filename) as f:
        # iterate over non-blank lines
        for line in ifilter(None, imap(str.strip, f)):
            if line.startswith('> '):
                last_result = interp.eval(line[2:])
            else:
                num_tests += 1
                try:
                    assert line == repr(last_test_result)
                except AssertionError, e:
                    print e.message
                else:
                    num_passed += 1
    print("Ran {} tests, {} passed".format(num_tests, num_passed))

...这只是假设任何结果行都引用了前面的测试。

我会避免使用 .readlines() ,除非您从一次提供整个文件中获得一些特定的好处。

我也改了比较看结果的表示,这样就可以区分输出类型,即

'6' + '2'
> '62'

60 + 2
> 62
于 2012-07-11T15:49:16.350 回答
0

将所有内容读入一个数组就相当于随机访问:您使用数组索引向下移动数组,并且在任何时候您都可以检查接下来的内容并在必要时进行备份。

如果您可以在不备份的情况下执行任务,则不需要随机访问,没有它会更干净。在您的示例中,您的语法似乎始终是单行 (?) 表达式,后跟预期的响应。因此,我会编写一个顶级循环,每个表达式-值对迭代一次,并根据需要读取行。如果要支持多行表达式和结果,可以编写单独的函数来读取每个:一个读取完整的表达式,一个读取结果(直到下一个空白行)。重要的是它们应该能够根据需要消耗尽可能多的输入,并将输入指针保持在合理的状态以进行下一个输入。

于 2012-07-11T16:30:24.520 回答