這個作業屬於哪個課程 | http://dwz.date/cts4 |
---|---|
這個作業要求在哪里 | https://edu.cnblogs.com/campus/fzu/SE2020/homework/11167 |
這個作業的目標 | 解析json 熟悉命令行操作 規范編程習慣 學習測試項目 github使用 |
學號 | 031802516 |
語言 | python |
PSP 表格
PSP2.1 | Personal Software Process Stages | 預估耗時(分鍾) | 實際耗時(分鍾) |
---|---|---|---|
Planning | 計划 | 60 | 90 |
Estimate | 估計這個任務需要多少時間 | 10 | 20 |
Development | 開發 | ||
Analysis | 需求分析 (包括學習新技術) | 40 | 60 |
Design Spec | 生成設計文檔 | 15 | 15 |
Design Review | 設計復審 | 10 | 10 |
Coding Standard | 代碼規范 (為目前的開發制定合適的規范) | 10 | 10 |
Design | 具體設計 | 40 | 40 |
Coding | 具體編碼 | 500 | 300 |
Code Review | 代碼復審 | 100 | 60 |
Test | 測試(自我測試,修改代碼,提交修改) | 100 | 200 |
Reporting | 報告 | 40 | 30 |
Test Report | 測試報告 | 30 | 20 |
Size Measurement | 計算工作量 | 15 | 15 |
Postmortem & Process Improvement Plan | 事后總結, 並提出過程改進計划 | 60 | 90 |
合計 | 1030 | 960 |
解題思路
剛開始看到題目的時候有點懵,看到控制台命令有點不知所措,在舍友的示范下很快就明白了。多次閱讀題目后就有了比較清晰的思路,其實這題就是解析 json。但是由於要處理的數據量很大,所有要在初次訪問的時候就把數據保存在本地起到緩存作用,並且在讀文件時也采用readline() 防止內存炸掉。然后就開始復習了 json 的格式、python里一些數據結構之間的轉換以及正則表達式的規則,解析完json就是完成了內核的,但是要編寫命令提供交互,然后又去查詢了有關py命令行參數編寫的方法。
設計實現過程
代碼組織
抓住這個編程題的核心就是解析json, 於是就先編寫解析一個json文件的函數JsonAnalyse() 提取有用的信息, 在把JsonAnalyse 函數裝進另一個尋找json文件的函數AnalyseAll(),實現對目標目錄下所有的json文件的解析。把這些函數和相關的數據分裝在類 Data里,在初始化init 的時候調用這些函數,解析並把所的數據以json的格式(解析采用 正則表達式 )存在本地,在后續的查詢時直接調用本地文件,起到緩存的作用,最后再編寫下命令行接口就好了。
核心代碼init函數流程圖:
代碼說明
思路
先編寫好解析一個json文件的函數,把要用的信息提取(采用正則表達式法 )出來並保存在類里定義的變量里,再把這個函數裝進一個搜索文件夾中存在.json文件的函數就形成了一個解析一個文件夾的函數,最后配合一個把數據保存本地的函數,在初始化(構造)結束時就完成了所有解析。二次訪問的時候就是把保存的文件提取出來,進行查詢操作(查詢函數過於簡單就不再貼出來了,本質就是 json 和 dict 之間的轉換)。整體上來說就是從核心突破,化繁為簡,完成核心功能函數再一步步拓展,就可以完成整個的項目。
TotalAnalyse 函數
def TotalAnalyse(self,addr:str):#解析文件夾中的所有json
for root, dic, files in os.walk(addr):
for f in files:
if f[-5:] == ".json":
jsonPath = f
self.JsonAnalyse(addr, jsonPath)
JsonAnalyse 函數
def JsonAnalyse(self, addr:str,jPath:str):#單個json解析函數
f = open(addr + '\\' + jPath, 'r', encoding='utf-8')
try:
while True:
stmp = f.readline()
if stmp:
pattern = re.compile('"login":".*?",')
res=pattern.search(stmp).group(0)
rkey1 = res[9:-2]
pattern = re.compile('"name":".*?",')
res = pattern.search(stmp).group(0)
rkey2 = res[8:-2]
pattern = re.compile('"type":".*?",')
res = pattern.search(stmp).group(0)
rkey3 = res[8:-2]
if not rkey3 in ['PushEvent','IssueCommentEvent',
'IssuesEvent','PullRequestEvent']:
continue
if not rkey1 in self.uEvent.keys():
event = {'PushEvent':0,'IssueCommentEvent':0,
'IssuesEvent':0,'PullRequestEvent':0}
self.uEvent[rkey1] = event
if not rkey2 in self.rEvent.keys():
event = {'PushEvent':0,'IssueCommentEvent':0,
'IssuesEvent':0,'PullRequestEvent':0}
self.rEvent[rkey2] = event
if not rkey1+'&'+rkey2 in self.urEvent.keys():
event = {'PushEvent': 0, 'IssueCommentEvent': 0,
'IssuesEvent': 0, 'PullRequestEvent': 0}
self.urEvent[rkey1 + '&' + rkey2] = event
self.uEvent[rkey1][rkey3] += 1
self.rEvent[rkey2][rkey3] += 1
self.urEvent[rkey1 + '&' + rkey2][rkey3] += 1
else:
break
except:
pass
finally:
f.close()
SaveToLocal 函數
def SaveToLocal(self):
try:
with open('user.json', 'w', encoding = 'utf-8') as f:
json.dump(self.uEvent, f)
with open('repo.json', 'w', encoding = 'utf-8') as f:
json.dump(self.rEvent, f)
with open('userepo.json', 'w', encoding = 'utf-8') as f:
json.dump(self.urEvent, f)
except:
raise RuntimeError("save error")
finally:
f.close()
單元測試截圖和描述
單元測試概述
分別對Data類測試初始化(包含了json 的讀取、解析以及寫入)、 三個查詢(QueryByUser,QueryByRepo,QueryByUserAndRepo),在.json 文件插入幾條特定的數據,在測試中采用斷言的方式來判斷是否成功完成對應的操作。一共4個test函數全部通過。
單元測試代碼截圖
單元測試運行截圖
單元測試覆蓋率
單元測試覆蓋率優化和性能測試
性能測試提升
由於python 雞肋的多線程,導致性能提升胎死腹中,不得不感嘆一句 java 才是永遠的神。
既然無法在多線程下手,就要從解析單個json突破。原來解析json模式為 讀取文件->字符串->json -> json讀取,改成了讀取文件->字符串->正則表達式 模式。不僅可以加快索引的時間,也可以省區 str 與 dict 轉換的時間。 經測試,在讀取141MB文件時新模式用時1.29s,而舊模式用時2.19s ;在讀取 1 GB 文件的情況下 新模式用時 5.36s 而舊模式用時 13.07s;可知,新模式的性能相對與舊模式提升了60%以上,並且讀取的文件越大提升的性能就會越顯著。在10GB 文件下,程序測試仍然會運行91.75s, 還是不沒有達到理想要求。
值得一提的是,助教提供的程序有很多重復的json值訪問,只要把這些值先預存起來也可以在一定程度上提高程序的性能。
- 舊模式 VS 新模式(141MB文件)
- 舊模式 VS 新模式(1GB文件)
性能測試截圖
代碼規范的鏈接
https://github.com/jieblue/2020-personal-python/blob/master/codestyle.md
總結
這次作業代碼的兩個核心內容解析json 和命令行參數編寫,由於以前有做過解析json的作業,所以沒有碰到太大的問題,命令行參數編寫是第一次碰到,有去查閱資料,也參考了助教提供的模板,最后編寫成功。還有也是第一冊接觸到單元測試和性能測試,也是查閱了一些資料(游走於csdn 和 cnblog之間)才完成這些測試。這次作業學到了很多新的知識,也復習了以前學的一些知識,收獲很多。