第一次个人编程作业
| 这个作业属于哪个课程 | https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/ |
|---|---|
| 这个作业要求在哪里 | https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13229/ |
| 这个作业的目标 | 完成个人编程作业编码部分,并使用Github来管理源代码和测试用例和完成测试 |
github链接:https://github.com/le4dora/3122004547
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 50 | 75 |
| Estimate | 估计这个任务需要多少时间 | 20 | 20 |
| Development | 开发 | 120 | 143 |
| Analysis | 需求分析 (包括学习新技术) | 40 | 30 |
| Design Spec | 生成设计文档 | 20 | 20 |
| Design Review | 设计复审 | 30 | 30 |
| Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
| Design | 具体设计 | 40 | 50 |
| Coding | 具体编码 | 45 | 45 |
| Code Review | 代码复审 | 20 | 15 |
| Test | 测试(自我测试,修改代码,提交修改) | 60 | 60 |
| Reporting | 报告 | 60 | 55 |
| Test Repor | 测试报告 | 45 | 60 |
| Size Measurement | 计算工作量 | 10 | 15 |
| Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 35 | 50 |
| 合计 | 605 | 678 |
二、模块接口的设计和实现过程
1.代码组织方式
共分为四个模块:
- 读文件:读取原文本和抄袭文本,并过滤掉无效字符
- 写文件:将余弦相似度输出到一个结果文件
- 分词:利用jieba模块完成中文分词
- 计算余弦相似度:分词后向量化,利用余弦定理计算相似度
2.函数间的关系

三、模块接口部分的性能改进
1.代码质量分析
利用pycharm原生的代码检查工具。

2.性能分析
利用python自带的cProfile库。
cProfile.run('readFile(oriFile, difFile)')
cProfile.run('process(content, content_dif)')
cProfile.run('cosine_similarity(seg_list, seg_list_dif)')
cProfile.run('outputFile(dist, outFile)')
依次分析四个模块的性能消耗。




可以发现,消耗时间最长的是第三个函数计算余弦相似度模块和第二个函数分词模块。
- 计算余弦相似度可以使用 Counter 提升频率统计效率
优化后:
![]()
时间明显缩短 - 超长文本中可能会出现重复性词语,造成重复调用jieba.cut,可以通过缓存分词结果来减少性能消耗。
优化后:
![]()
四、模块部分单元测试展示
1.单元测试代码及思路
第一部分:测试读文件时可能会出现的问题
import os
from main import readFile
class TestReadFile(unittest.TestCase):
def setUp(self):
# 创建临时文件用于测试
self.orig_file = 'test_ori.txt'
self.dif_file = 'test_dif.txt'
with open(self.orig_file, 'w', encoding='utf-8') as f:
f.write("awuz你现在是真的火了,平时一定要注意自己的言行举止,不要让人以为你是一个多坏的人,你不知道有多少眼睛盯着你,看着你出错,我知道你是个很真性情的人,也希望你能保持这份直率,但你更要严格要求自己,谨言慎行,起到艾斯比的表率作用,这样才能走的更远。你记住、鱼越大,刺越大;刺越大,肉越少;肉越少,刺越小;刺越小,鱼越小,所以鱼越大,鱼越小。\n")
with open(self.dif_file, 'w', encoding='utf-8') as f:
f.write("awuz你现在语是真的火了,平时一定要注意自己囖的言行举止,不要让人以为你是一个玉玉多坏的人,你不知道即可有多少眼睛盯着你,看着你出错,我知道你是记得个很真性情的人,也希望你能保持这特尔份直率,但你更要严惹格要求自己,谨言的慎行,起到艾斯比的表率哦婆婆作用,这样才能走的打更远。你记住、鱼越大,刺越大;刺越大,肉越少;肉越少,刺越小;刺越我小,鱼越小,所以鱼越大,鱼越小。")
def tearDown(self):
# 删除测试文件
os.remove(self.orig_file)
os.remove(self.dif_file)
def test_read_file(self):
content_r, content_r_dif = readFile(self.orig_file, self.dif_file)
self.assertEqual(content_r, 'awuz你现在是真的火了平时一定要注意自己的言行举止不要让人以为你是一个多坏的人你不知道有多少眼睛盯着你看着你出错我知道你是个很真性情的人也希望你能保持这份直率但你更要严格要求自己谨言慎行起到艾斯比的表率作用这样才能走的更远你记住鱼越大刺越大刺越大肉越少肉越少刺越小刺越小鱼越小所以鱼越大鱼越小')
self.assertEqual(content_r_dif, "awuz你现在语是真的火了平时一定要注意自己囖的言行举止不要让人以为你是一个玉玉多坏的人你不知道即可有多少眼睛盯着你看着你出错我知道你是记得个很真性情的人也希望你能保持这特尔份直率但你更要严惹格要求自己谨言的慎行起到艾斯比的表率哦婆婆作用这样才能走的打更远你记住鱼越大刺越大刺越大肉越少肉越少刺越小刺越我小鱼越小所以鱼越大鱼越小")
def test_empty_files(self):
with open(self.orig_file, 'w', encoding='utf-8') as f:
f.write("")
with open(self.dif_file, 'w', encoding='utf-8') as f:
f.write("")
content_r, content_r_dif = readFile(self.orig_file, self.dif_file)
self.assertEqual(content_r, '')
self.assertEqual(content_r_dif, '')
def test_special_characters(self):
with open(self.orig_file, 'w', encoding='utf-8') as f:
f.write("!@#$%^&*()+_[]{}|;:',.<>/?`~")
with open(self.dif_file, 'w', encoding='utf-8') as f:
f.write("`~!@#$%^&*()+_-=<>?/:;,.|[]{}")
content_r, content_r_dif = readFile(self.orig_file, self.dif_file)
self.assertEqual(content_r, '')
self.assertEqual(content_r_dif, '')
if __name__ == '__main__':
unittest.main()
- setUp 方法:
在每个测试运行之前创建临时文件,并写入测试数据。
- tearDown 方法:
在每个测试之后删除临时文件,以清理环境。
- test_read_file 方法:
测试函数对正常文件的处理,包括混合的空格和标点符号,检查函数是否能正确去掉无效字符并返回期望的结果。
- test_empty_files 方法:
测试处理空文件的情况,确保函数能正确处理空输入并返回空字符串。
- test_special_characters 方法:
测试文件包含特殊字符的情况,检查函数是否能正确去除这些字符并返回空字符串。
第二部分:测试分词模块可能会出现的问题
import jieba
from main import process
class TestTextProcessing(unittest.TestCase):
def setUp(self):
self.text1 = "awuz你现在是真的火了平时一定要注意自己的言行举止不要让人以为你是一个多坏的人你不知道有多少眼睛盯着你看着你出错我知道你是个很真性情的人也希望你能保持这份直率但你更要严格要求自己谨言慎行起到艾斯比的表率作用这样才能走的更远你记住鱼越大刺越大刺越大肉越少肉越少刺越小刺越小鱼越小所以鱼越大鱼越小"
self.text2 = "awuz你现在语是真的火了平时一定要注意自己囖的言行举止不要让人以为你是一个玉玉多坏的人你不知道即可有多少眼睛盯着你看着你出错我知道你是记得个很真性情的人也希望你能保持这特尔份直率但你更要严惹格要求自己谨言的慎行起到艾斯比的表率哦婆婆作用这样才能走的打更远你记住鱼越大刺越大刺越大肉越少肉越少刺越小刺越我小鱼越小所以鱼越大鱼越小"
self.expected_output1 = list(jieba.cut(self.text1, cut_all=False))
self.expected_output2 = list(jieba.cut(self.text2, cut_all=False))
print(self.expected_output1)
print(self.expected_output2)
def test_process(self,):
# 调用 process 函数
seg_list_p, seg_list_dif_p = process(self.text1, self.text2)
# 断言结果是否符合预期
self.assertEqual(seg_list_p, self.expected_output1)
self.assertEqual(seg_list_dif_p, self.expected_output2)
if __name__ == '__main__':
unittest.main()
第三部分:余弦相似度计算
import unittest
from collections import Counter
import numpy as np
from main import cosine_similarity # 替换为实际模块路径
class TestCosineSimilarity(unittest.TestCase):
def test_cosine_similarity(self):
# 比较
seg_list_cos1 = ['现在', '言行举止', '一个多', '出错', '知道']
seg_list_cos2 = ['现在', '一个多', '出错', '知道', '即可']
# 预期的余弦相似度值
expected_similarity = 0.8 # 这是一个示例值,根据实际计算结果进行调整
# 调用余弦相似度函数
result = cosine_similarity(seg_list_cos1, seg_list_cos2)
# 断言结果是否符合预期
self.assertAlmostEqual(result, expected_similarity, places=2)
# 完全相同的情况
seg_list_cos3 = seg_list_cos1 = ['现在', '言行举止', '一个多', '出错', '知道']
expected_similarity = 1.0
result = cosine_similarity(seg_list_cos1, seg_list_cos3)
self.assertAlmostEqual(result, expected_similarity, places=2)
# 完全不同的情况
seg_list_cos4 = ['类目', '笑嘻嘻']
expected_similarity = 0.0
result = cosine_similarity(seg_list_cos1, seg_list_cos4)
self.assertAlmostEqual(result, expected_similarity, places=2)
# 空的情况
seg_list_cos5 = []
expected_similarity = 0.0
result = cosine_similarity(seg_list_cos1, seg_list_cos4)
self.assertAlmostEqual(result, expected_similarity, places=2)
if __name__ == '__main__':
unittest.main()
- 一共是四种情况,注释已经说明。
2.单元测试得到的测试覆盖率





浙公网安备 33010602011771号