亓雪冬 李霞


摘 要:針對(duì)Python程序自動(dòng)評(píng)測(cè)過程中被測(cè)試程序輸入輸出流重定向問題進(jìn)行研究。分析了基于操作系統(tǒng)輸入輸出重定向方法的缺點(diǎn)與不足,基于Python的動(dòng)態(tài)性提出了Python偽裝文件類對(duì)象輸入輸出重定向方法。設(shè)計(jì)并實(shí)現(xiàn)了滿足標(biāo)準(zhǔn)輸入輸出流接口特性的偽裝文件對(duì)象,在Python程序內(nèi)部接管輸入流和輸出流,提高了重定向接口的靈活性,減少磁盤空間占用,嚴(yán)格限制輸出流數(shù)據(jù)尺寸,保障評(píng)測(cè)過程穩(wěn)定進(jìn)行。該方法在實(shí)際應(yīng)用中效果良好。
關(guān)鍵詞: Python; Online judge; 自動(dòng)測(cè)評(píng); 輸入輸出重定向
中圖分類號(hào): TP 393
文獻(xiàn)標(biāo)志碼: A
Abstract: The paper studies problems about the IO stream redirection in the automatic evaluation of python language programs.Thepaper analyzes and points out shortcomings of the OS-based IO redirection method, and proposes an IO redirection method using python masquerading file class object. The paper designsand implement masquerading file class objectwhich meets the characteristics of standard IO stream interfaces. This object takes over IO streams inside a python program,increases flexibility of the redirection interface, reduces disk space usage, limits the output stream data size strictly, ensures the stability of the automatic evaluationprocess. This method works well in practical applications.
Key words: Python; Online judge; automatic evaluation; IO redirection
0 引言
程序設(shè)計(jì)類課程是當(dāng)前大學(xué)教育課程體系重要組成部分,是重要的通識(shí)教育類課程,該類課程著重培養(yǎng)學(xué)生使用程序設(shè)計(jì)語言和算法解決實(shí)際問題的能力,使學(xué)生深刻理解計(jì)算機(jī)自動(dòng)計(jì)算工作原理,建立計(jì)算思維為特征的普遍思維模式。近幾年,Python語言以其語法簡潔、易學(xué)易用、交互性、動(dòng)態(tài)性和具有豐富的第三方程序庫等特點(diǎn)逐漸被國內(nèi)外高校所認(rèn)可,開設(shè)了大量以Python語言的載體的程序設(shè)計(jì)類課程。嵩天等提出,Python語言是程序設(shè)計(jì)課程教學(xué)改革的理想選擇[1]。
為了提高學(xué)習(xí)效率并提升教學(xué)效果,程序設(shè)計(jì)類課程均需快速對(duì)學(xué)生編寫的程序進(jìn)行自動(dòng)評(píng)判[2-3]。程序自動(dòng)測(cè)評(píng)實(shí)現(xiàn)了批改和反饋的自動(dòng)化[4-6],是維護(hù)課堂秩序、保障教學(xué)效果和提升教學(xué)質(zhì)量的重要技術(shù)手段。程序自動(dòng)測(cè)評(píng)涉及到多種關(guān)鍵技術(shù),如標(biāo)準(zhǔn)輸入輸出流重定向、文件輸出重定向和管理,程序執(zhí)行時(shí)間和內(nèi)存開銷管理、GUI評(píng)測(cè)方法等等。此外,不同編程語言的程序自動(dòng)評(píng)判時(shí)具有其自身的特點(diǎn),本文針對(duì)Python語言程序自動(dòng)評(píng)測(cè)中,被測(cè)試程序輸入輸出流的重定向問題進(jìn)行研究,在分析傳統(tǒng)的基于操作系統(tǒng)輸入輸出重定向方法的基礎(chǔ)上,提出了Python偽裝文件類對(duì)象輸入輸出重定向方法,該方法在程序內(nèi)部而不是進(jìn)程外部對(duì)被輸入輸出數(shù)據(jù)別進(jìn)行管理,具有更強(qiáng)的靈活性和自主性。
1 操作系統(tǒng)輸入輸出重定向方法
學(xué)生編寫的Python程序通常包含輸入和輸出語句,程序在正常執(zhí)行時(shí),輸入語句會(huì)阻塞程序等待用戶在標(biāo)準(zhǔn)控制臺(tái)中輸入數(shù)據(jù),輸出語句需要向標(biāo)準(zhǔn)控制臺(tái)輸出數(shù)據(jù),因此需要用戶與程序之間的交互處理。
然而在程序自動(dòng)評(píng)測(cè)時(shí),這個(gè)過程需要自動(dòng)完成,在實(shí)踐中常常將標(biāo)準(zhǔn)輸入流、標(biāo)準(zhǔn)輸出流和標(biāo)準(zhǔn)錯(cuò)誤流重定向到相關(guān)文件中。操作系統(tǒng)一般提供了基于文件的輸入輸出重定向功能,例如學(xué)生的Python程序?yàn)閟tud.py,執(zhí)行程序的命令可寫為:
Pythonstud.py< in.txt> out.txt2>&1
執(zhí)行時(shí),文件in.txt的內(nèi)容將被重定向到標(biāo)準(zhǔn)輸入流中,因此輸入語句會(huì)從文件in.txt中直接讀取數(shù)據(jù)而無需等待用戶輸入;程序運(yùn)行時(shí)產(chǎn)生的標(biāo)準(zhǔn)輸出流和標(biāo)準(zhǔn)錯(cuò)誤流將重定向到文件out.txt中。
然而,由于被測(cè)試程序編寫不當(dāng)導(dǎo)致程序陷入死循環(huán)中,并在死循環(huán)中持續(xù)向標(biāo)準(zhǔn)輸出流輸出數(shù)據(jù)時(shí),如不對(duì)輸出數(shù)據(jù)尺寸進(jìn)行限制,會(huì)在短時(shí)間內(nèi)產(chǎn)生超大尺寸文件而占滿磁盤空間導(dǎo)致系統(tǒng)崩潰。以筆者電腦為例,CPU為Intel Core i7-8565U 1.8 GHz,內(nèi)存8 G,硬盤512 G。在每次循環(huán)中分別輸出32字節(jié)、64字節(jié)、128字節(jié)和256字節(jié)的數(shù)據(jù),程序運(yùn)行5秒后被強(qiáng)制關(guān)閉。每種情況下均實(shí)驗(yàn)20次并統(tǒng)計(jì)平均值,程序產(chǎn)生輸出數(shù)據(jù)文件尺寸,如圖1所示。
隨著單次輸出字節(jié)數(shù)的增加,程序產(chǎn)生輸出文件的尺寸也隨之增大,兩者之間基本呈線性關(guān)系。以單次輸出128字節(jié)為例,程序運(yùn)行5秒產(chǎn)生733 M字節(jié)數(shù)據(jù),平均1秒產(chǎn)生146.6 M字節(jié)的數(shù)據(jù)。可見必須采取措施對(duì)傳向標(biāo)準(zhǔn)輸出流的數(shù)據(jù)字節(jié)數(shù)進(jìn)行限制。
操作系統(tǒng)輸入輸出重定向方式的優(yōu)點(diǎn)是:1)由操作系統(tǒng)直接支持,無需改動(dòng)已編好的程序;2)使用文件作為重定向的源和目標(biāo),配置和查看數(shù)據(jù)非常方便。但這種方式也存在一些缺點(diǎn),主要體現(xiàn)在:1)輸入數(shù)據(jù)需要提前保存在相關(guān)文件中,程序運(yùn)行時(shí)輸出數(shù)據(jù)也將寫入指定文件,不僅占用磁盤空間,頻繁文件讀寫也易造成磁盤損壞;2)輸入數(shù)據(jù)通過文件讀取而非通過程序接口讀取,缺乏可編程能力,靈活性較弱;3)輸出數(shù)據(jù)流向文件寫入時(shí)未加入任何限制措施,若測(cè)試程序陷入死循環(huán),可能導(dǎo)致輸出文件尺寸過大而占滿磁盤空間,影響系統(tǒng)運(yùn)行的穩(wěn)定性。
2 Python偽裝文件類對(duì)象輸入輸出重定向方法
筆者從Python的動(dòng)態(tài)性和Python文件類對(duì)象接口角度出發(fā),提出了基于偽裝文件類對(duì)象的輸入輸出重定向方法,這種方法將Python程序內(nèi)部的標(biāo)準(zhǔn)輸入流對(duì)象(sys.stdin)、輸出流對(duì)象(sys.stdout)和錯(cuò)誤流對(duì)象(sys.stderr)等對(duì)象引用重置到自行實(shí)現(xiàn)的偽裝的文件類對(duì)象,實(shí)現(xiàn)了更加普遍適用的重定向方式,解決了上面提出的各種問題。
實(shí)現(xiàn)原理為:由于Python的動(dòng)態(tài)性,Python中任何在方法上與文件類似的對(duì)象都可以充當(dāng)標(biāo)準(zhǔn)流,這與對(duì)象的實(shí)際數(shù)據(jù)類型無關(guān),而取決于接口(也被稱為協(xié)議)。具體來說,任何定義了類似于文件write方法的對(duì)象都可以賦值給sys.stdout,則所有標(biāo)準(zhǔn)輸出數(shù)據(jù)將發(fā)送到該對(duì)象的write方法上;任何定義了類似于文件read方法的對(duì)象都可以賦值給sys.stdin,則所有標(biāo)準(zhǔn)輸入數(shù)據(jù)將通過該對(duì)象的read方法獲取。由于Python中的input和print兩個(gè)輸入輸出函數(shù)只是簡單的調(diào)用sys.stdin和sys.stdout所引用對(duì)象的write和readline方法,而程序運(yùn)行錯(cuò)誤也是通過sys.stderr所引用對(duì)象的write方法輸出,因此我們可以使用該技術(shù)設(shè)計(jì)實(shí)現(xiàn)偽裝文件類對(duì)象來攔截標(biāo)準(zhǔn)輸入輸出流和錯(cuò)誤流。
偽裝輸入文件類對(duì)象的程序?yàn)椋?/p>
class Input:
def __init__(self, data=[]):
self.data = data
self.index = 0
def readline(self):
if self.index text = str(self.data[self.index]) self.index+=1 else: text = '' return text 偽裝輸入文件類類名為Input,對(duì)象初始化方法__init__一方面設(shè)定數(shù)據(jù)列表,將其存儲(chǔ)在self.data屬性中,這個(gè)列表表示輸入流中可被讀取的數(shù)據(jù)行,列表的每一個(gè)元素代表一行數(shù)據(jù);另一方面設(shè)定待讀取數(shù)據(jù)的位置self.index為0,表示即將要讀取列表中下標(biāo)為0的元素?cái)?shù)據(jù)。 被測(cè)試程序運(yùn)行前,需要使用Input類的實(shí)例對(duì)象對(duì)sys.stdin變量賦值,修改標(biāo)準(zhǔn)輸入流向。如sys.stdin = Input([10,20,'abc']),列表中的3個(gè)元素表示輸入流中的3行數(shù)據(jù)。被測(cè)試程序執(zhí)行input函數(shù)時(shí),會(huì)調(diào)用上述實(shí)例對(duì)象的readline方法,讀取列表中的下一個(gè)數(shù)據(jù)。如果列表的所有數(shù)據(jù)都已讀完,則readline返回空字符串,表示沒有讀到數(shù)據(jù)。偽裝輸入文件類的設(shè)計(jì)使得輸入數(shù)據(jù)不需要提前寫入文件,而是采用Python標(biāo)準(zhǔn)數(shù)據(jù)結(jié)構(gòu)的形式保存,操作和讀取都具有很高的靈活性。比如Input([10,20,'abc']*3),對(duì)輸入數(shù)據(jù)列表進(jìn)行運(yùn)算,快速產(chǎn)生9行輸入數(shù)據(jù)。 偽裝輸出文件類對(duì)象的程序?yàn)椋?/p> class Output: def __init__(self,MaxSize=4096): self.MaxSize = MaxSize self.text = '' def write(self,text): n = self.MaxSize-len(self.text) if n>0: self.text += text[0:n] 偽裝輸出文件類類名為Output,對(duì)象初始化方法__init__可設(shè)定輸出文件的最大尺寸self.MaxSize,默認(rèn)輸出文件最大尺寸限制為4K字節(jié)。當(dāng)程序向輸出流中發(fā)送數(shù)據(jù)時(shí),會(huì)調(diào)用該類實(shí)例對(duì)象的write方法,將輸出數(shù)據(jù)寫入對(duì)象的self.text屬性。write方法累計(jì)統(tǒng)計(jì)輸出數(shù)據(jù)字節(jié)數(shù),若超過了最大尺寸self.MaxSize的預(yù)設(shè)值,超出部分的數(shù)據(jù)將被丟棄。 另外,被測(cè)試程序運(yùn)行前,需要使用Output類的實(shí)例對(duì)象對(duì)sys.stdout變量賦值,修改標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤流向。如sys.stdout = Output() ; sys.stderr = sys.stdout。被測(cè)試程序執(zhí)行時(shí),print函數(shù)等向標(biāo)準(zhǔn)輸出流以及程序出錯(cuò)時(shí)向標(biāo)準(zhǔn)錯(cuò)誤流發(fā)送的數(shù)據(jù)會(huì)被發(fā)送到上述實(shí)例對(duì)象,保存到self.text屬性中。被測(cè)試程序結(jié)束后,可以通過讀取上述實(shí)例對(duì)象的self.text屬性獲取程序?qū)嶋H輸出。偽裝輸出文件類的設(shè)計(jì)使得程序輸出不是寫入文件中,而存儲(chǔ)在內(nèi)存中,不占用磁盤空間,而且可以嚴(yán)格限制輸出數(shù)據(jù)最大字節(jié)數(shù),確保系統(tǒng)穩(wěn)定運(yùn)行。 3 總結(jié) 本文針對(duì)Python語言程序自動(dòng)評(píng)測(cè)中,被測(cè)試程序輸入輸出流的重定向問題進(jìn)行研究,闡述了傳統(tǒng)的基于操作系統(tǒng)的輸入輸出重定向方法,分析和總結(jié)了這種方法的缺點(diǎn)與不足,并基于Python的動(dòng)態(tài)性提出了Python偽裝文件類對(duì)象輸入輸出重定向方法,通過設(shè)計(jì)實(shí)現(xiàn)滿足標(biāo)準(zhǔn)輸入輸出流接口特性的文件對(duì)象,在Python程序內(nèi)部接管輸入流和輸出流數(shù)據(jù),有效改善輸入輸出重定向的靈活性。該方法在實(shí)際應(yīng)用中效果良好,對(duì)其它程序語言自動(dòng)測(cè)評(píng)中輸入輸出流重定向問題的研究也具有一定的啟發(fā)性作用。 參考文獻(xiàn) [1] 嵩天,黃天羽,禮欣.Python語言:程序設(shè)計(jì)課程教學(xué)改革的理想選擇[J].中國大學(xué)教學(xué),2016(2):42-47. [2] Kurnia A,Lim A,Cheang B. Online judge[J]. Computers & Education,2001,36(4): 299-315. [3] 杜祥軍,李建波,李敏,等.基于Online Judge的計(jì)算機(jī)類課程教學(xué)評(píng)價(jià)方法研究[J].計(jì)算機(jī)教育,2019(3):55-57. [4] 陳榮欽,胡永良,應(yīng)建健,等.在線評(píng)測(cè)系統(tǒng)中的源碼相似度檢測(cè)研究與實(shí)現(xiàn)[J].實(shí)驗(yàn)技術(shù)與管理,2014,31(4):109-111. [5] 崔舒寧,吳寧,葉丹.建立抽象語法樹模型評(píng)測(cè)C++代碼[J].計(jì)算機(jī)應(yīng)用,2015,35(S1):183-185. [6] 張曉瑩,盧衛(wèi),程一艦,等.面向慕課的在線SQL自動(dòng)評(píng)測(cè)系統(tǒng)及應(yīng)用[J].實(shí)驗(yàn)技術(shù)與管理,2018,35(4):16-22. (收稿日期: 2019.08.26)