使用带有索引或解析行的readlines()?

时间:2012-07-11 12:42:05

标签: python

我正在创建一个简单的测试函数,它通过从文件读取要评估的表达式和预期结果来断言我正在开发的解释器的输出是正确的,就像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()中使用itertools作为成对行,但我稍后会研究这种方法。)

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

我真的很疑惑,因为我的感觉恰恰相反,我认为一次解析一条线似乎更干净,所以一切都完成了。我通常避免在Python中使用数组中的索引,而更喜欢使用迭代器和生成器。也许不可能回答和猜测这个人在思考这个主观意见时的想法,但如果有一些一般的建议我会很高兴听到它。

3 个答案:

答案 0 :(得分:1)

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

赞成阅读整个数组和索引的论据是,当与next(f)循环组合时,使用for可能不清楚;选项可以是用for替换while True循环,也可以在循环中完整记录您在next上调用f的内容:

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

正如Jonas建议你可以通过自己压缩输入来实现这一目标(如果你确定输入将始终由行测试/响应/测试/响应等组成):

for test, response in zip(f, f):               # Python 3
for test, response in itertools.izip(f, f):    # Python 2

答案 1 :(得分: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

答案 2 :(得分:0)

将所有内容读入数组会为您提供相应的随机访问权限:您可以使用数组索引向下移动数组,并且可以随时检查下一步并在必要时进行备份。

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