软工实践第一次个人编程作业
这个作业属于哪个课程 | 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之间)才完成这些测试。这次作业学到了很多新的知识,也复习了以前学的一些知识,收获很多。