2012-07-11 21 views
4

我正在做一個簡單的測試函數,聲明我正在開發的解釋器的輸出是正確的,通過從文件中讀取要評估的表達式和預期的結果,非常類似於python的doctest。這是方案,所以輸入文件的一個例子是使用帶有索引或分析行的readlines()?

> 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()代替,然後在結果列表上使用索引。 (我也告訴我,我可以在itertools使用groupby()爲兩兩行,但後來我去調查這種做法。)

我們的問題,我很好奇,爲什麼這種做法會更好,但我的互聯網連接在火車上是片狀的,我無法問,所以我會在這裏問它。爲什麼最好用readlines()來讀取所有內容,而不是在每一行都進行解析,因爲它們是在飛行中讀取的?

我真的好奇,因爲我的感覺恰恰相反,我認爲一次一個地解析一行,看起來更清晰,因此一切都完成了。我通常避免在Python中使用數組中的索引,而更喜歡使用迭代器和生成器。如果這是一種主觀意見,也許不可能回答和猜測這個人的想法,但如果有一些一般性建議,我很樂意聽到它。

+2

爲什麼你首先使用'response_next'?你可以簡單地檢查一行是否以'>開頭,並基於此行動。如果是,則是解釋器輸入。如果它不是,那麼它是解釋器輸出或空行(你可以跳過)。如果這是不可能的,那麼你確實可以讀入整個文件並一次處理多行。對於這種方法,文件似乎不會太大。 – 2012-07-11 12:55:58

+0

'readlines'很好,如果你保證文件很小,你需要在文件中跳過很多。在這裏你描述的情況下,我寧願使用'next(f)'方法,因爲只要你需要實際處理一個大文件,習慣就會延續下去。 – mgilson 2012-07-11 12:57:15

+0

@SimeonVisser我想要在下面的行中聲明輸出,而在它之前沒有任何空白行,這就是爲什麼我添加了標誌來強制它實際上在下面的行。只是我自己的設計決定。可能是不必要的:-) – 2012-07-11 13:04:48

回答

0

讀一切到一個數組給您的隨機存取相當於:你使用數組索引向下移動數組,並且在任何時候,您都可以根據需要檢查下一步和備份。

如果您可以在不備份的情況下執行任務,則不需要隨機訪問,如果沒有備份,它會更乾淨。在你的例子中,你的語法似乎總是一個單行(?)表達式,然後是預期的響應。所以,我會寫一個頂級循環,每個表達式 - 值對迭代一次,根據需要讀取行。 如果您想要支持多行表達式和結果,您可以編寫單獨的函數來讀取每一個函數:一個讀取完整表達式,一個讀取結果(直到下一個空行)。重要的是它們應該能夠根據需要消耗盡可能多的輸入,並使輸入指針保持在下一個輸入的合理狀態。

1

反覆處理輸入,而不是一次讀取整個輸入肯定會更多Pythonic;例如,如果輸入是控制檯,這將工作。

支持讀取整個數組和索引的觀點是,使用next(f)for循環組合時可能不清楚;選項會有要麼用while True更換for環或完整記錄您在f循環中調用next

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

約拿建議你可以做到這一點(如果你確定輸入將總是通過用本身壓縮和解輸入由線測試/響應/測試/響應等):

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

如何測試,在itertools.izip(f,f)中的響應:'(或者只是'zip「而不是py3k中的itertools thingy)? – 2012-07-11 16:01:44

+0

@JonasWielicki偉大的想法,謝謝。 – ecatmur 2012-07-12 08:36:34

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)) 

...這僅僅假定任何結果線指的是前述權利ng測試。

我會避免使用.readlines(),除非您從一次獲得整個文件獲得某些特定的好處。

我也改了比較,看看結果的表示,所以它可以輸出類型進行區分,即

'6' + '2' 
> '62' 

60 + 2 
> 62 
相關問題