中小学数学卷子自动生成程序——结对编程项目分析
1.简单介绍
本博客为对结对编程队友的个人项目代码的评价分析
项目名称:中小学数学卷子自动生成程序
主要功能:登录,切换难度,随机生成试卷,查重
2.开发环境
Windows10 python3.7
3.思路构造
队友思路就是将程序分为几个模块,包括:登陆,创建文件目录,切换难度,随机生成题目生成,查重等.队友的python源代码中除了主函数外,还有success_log_in(),make_direction(self),log_in()
4.关键代码分析介绍
第一部分 创建类及类函数
队友建立了一个Teacher类,存放每个教师的信息,以及初始化操作,初始化中会创建试卷存放目录,登录成功输出默认信息操作,并在类里建立了一些函数
1 class Teacher(object): 2 3 account = "" 4 password = 0 5 education = "" 6 location_teacher = '' 7 8 def __init__(self, account, password, education, location_teacher): 9 self.account = account 10 self.password = password 11 self.education = education 12 self.location_teacher = location_teacher 13 self.make_direction() 14 15 def success_log_in(self): 16 17 print("当前选择为" + self.education + "出题") 18 print("准备生成" + self.education + 19 "数学题目,请输入生成题目(数量题目数量为10-30,输入-1将退出当前用户,重新登录):") 20 global GLOBAL_VAR_LOCATION 21 GLOBAL_VAR_LOCATION = self.location_teacher 22 question_num_intput = input() 23 determine_question(question_num_intput, self.education) 24 25 def make_direction(self) -> object: 26 27 path = self.location_teacher 28 isexists = os.path.exists(path) 29 if isexists: 30 return False 31 else: 32 os.makedirs(path) 33 return True
一个简单的教师类,存放每个教师的信息,以及初始化操作,初始化中会创建试卷存放目录,登录成功输出默认信息操作,
类中四个元素
account:用户名。
password:密码。
education:教学阶段。
location_teacher:教师存储试卷的位置
第一个__init__(self, account, password, education, location_teacher)
用参数初始化教师信息,同时创建教室存放试卷的目录。
第二个def success_log_in(self)
提示用户登录成功,同时弹出提示信息。初次登录成功后,提示出题信息,此时显示为默认学历出题;全局变量g_location为储存试卷的位置,当登录一个老师时,即修改该位置;根据输入的数据,比如数字10-30,或切换为+学历,等等,进入determine_question()函数,这个函数通过判断输入的指令进行不同反应;
当然,在determine_question()函数中会对输入的指令进行正确性判断。
第三个def make_direction(self) -> object
创建目录位置,在上面的初始化函数中有调用;
如果该目录不存在,则创建每个用户存放题目的目录。
第二部分 设置教师账号信息和题目存储位置
1 GLOBAL_TEACHER_MESSAGE = \ 2 [Teacher("张三1", "123", "小学", 'C:\khl201926010226\张三1\\'), 3 Teacher("张三2", "123", "小学", 'C:\khl201926010226\张三2\\'), 4 Teacher("张三3", "123", "小学", 'C:\khl201926010226\张三3\\'), 5 Teacher("李四1", "123", "初中", 'C:\khl201926010226\李四1\\'), 6 Teacher("李四2", "123", "初中", 'C:\khl201926010226\李四2\\'), 7 Teacher("李四3", "123", "初中", 'C:\khl201926010226\李四3\\'), 8 Teacher("王五1", "123", "高中", 'C:\khl201926010226\王五1\\'), 9 Teacher("王五2", "123", "高中", 'C:\khl201926010226\王五2\\'), 10 Teacher("王五3", "123", "高中", 'C:\khl201926010226\王五3\\')] 11 12 GLOBAL_VAR_LOCATION = ''
第三部分 具体实现各功能的函数
1. log_in() 输入用户名和密码
1 def log_in(): 2 global GLOBAL_TEACHER_MESSAGE 3 try: 4 account_input, password_input = map(str, input("请输入用户名和密码\n").split()) 5 for case_teacher in GLOBAL_TEACHER_MESSAGE: 6 if case_teacher.account == account_input \ 7 and case_teacher.password == password_input: 8 case_teacher.success_log_in() 9 10 print("请输入正确的用户名、密码") 11 log_in() 12 13 except ValueError: 14 print("请输入正确的用户名、密码") 15 log_in()
通过try_except还有if语句进行条件判断并进入相关函数
如果用户名和密码匹配的话就进入success_log_in()函数,提示用户出题信息。
如果输入的不止用户名和密码,比如输入 张三1 123 123 或者 张三1 ,就会重新执行该登录函数;
如果输入的用户名和密码与老师库中的信息不匹配,也会重新执行该登录函数。
2.determine_question(question_num_intput, education) 判断用户指令
1 def determine_question(question_num_intput, education): 2 if question_num_intput.isdigit(): 3 num = int(question_num_intput) 4 if 10 <= num <= 30: 5 make_test(education, num) 6 else: 7 print("输入的题目数量不在指定范围之内,请重新输入") 8 question_num_intput = input() 9 determine_question(question_num_intput, education) 10 11 # 输入的不是纯数字,则判断是不是“切换为+XX”,执行切换功能或提示用户重新输入 12 # 输入-1也需要放在这里进行检测,如果输入-1就回到登录界面 13 # 输入的不是上述两种情况,则提示用户输入错误 14 else: 15 if question_num_intput == "-1": 16 log_in() 17 elif question_num_intput.find("切换为") == 0: 18 switch_education = question_num_intput.lstrip("切换为") 19 if switch_education == "小学" or \ 20 switch_education == "初中" or \ 21 switch_education == "高中": 22 print("准备生成" + switch_education + 23 "数学题目,请输入生成题目数量(题目数量为10-30,输入-1将退出当前用户,重新登录):") 24 question_num_intput = input() 25 determine_question(question_num_intput, switch_education) 26 else: 27 print("请输入小学、初中和高中三个选项中的一个") 28 question_num_intput = input() 29 determine_question(question_num_intput, education) 30 else: 31 print("输入错误,请重新输入") 32 question_num_intput = input() 33 determine_question(question_num_intput, education)
通过if_else语句判断用户输入的指令,进行下一步操作,是直接生成题目,还是切换到其他学历。
如果输入有效数组即10-30,就能进入make_question()函数,即生成题目函数;
如果输入“切换为+正确学历”,则会切换为这个需要切换的学历,重新执行该函数。
question_num_intput:输入的指令,函数会判断是数字还是字符串指令;
education:当前需要出题的学历。
3.make_test(education, num) 根据学历与数量生成试卷
1 global GLOBAL_VAR_LOCATION 2 summary_question = [] # 用于存放输出的试卷: 3 4 # 在此循环中,不断创建题目,每创建一个题目即进行一次查重 5 # 如果没有重复题目,则将此题目加入查重文档,同时将此题目加入试卷 6 for serial_number in range(0, num): 7 str_one_question = make_question(education, serial_number) 8 while check_duplicate(str_one_question): 9 str_one_question = make_question(education, serial_number) 10 location = GLOBAL_VAR_LOCATION + 'duplicate' + '.txt' 11 12 str_temp = str_one_question[3:] 13 file_duplicate = open(location, 'a') 14 file_duplicate.write(str_temp) 15 file_duplicate.close() 16 summary_question.append(str_one_question) 17 18 # 在当前教师对应的目录下创建以当前时间命名的txt文档,存放刚刚生成好的试卷 19 # 同时发出提示信息,提醒用户进行下一步操作 20 f_time = time.strftime('%Y-%m-%d-%H-%M-%S', time.localtime(time.time())) 21 location = GLOBAL_VAR_LOCATION + f_time + '.txt' 22 23 file_entry = open(location, 'a') 24 file_entry.write("注意题目中运算符号优先级:括号 > 三角函数 > 平方/根号 > 乘除 > 加减" + '\n' + '\n') 25 for every_question in summary_question: 26 file_entry.write(every_question + '\n') 27 file_entry.close() 28 print("题目输出完毕,题目输出至" + location) 29 print("可以输入数字10-30继续出题,输入-1退出,输入切换为+学历,切换不同学历等级的出题") 30 question_num_intput = input() 31 determine_question(question_num_intput, education)
通过for循环不断调用具体的生成题目的函数,每次函数返回一道题目,产生一次题目后会进行查重,如果没有重复则加入试卷;最后用.write()函数写入对应的文件夹中,同时会把所有的题目放入查重汇总的txt中。然后.close()关闭文件.其中education代表学历,num代表题目数量.
4.make_question(education, serial_number) 生成一道题目
1 def make_question(education, serial_number): 2 str_one_question = [] # 存放当前生成的题目 3 operate_number_num = random.randint(1, 5) # 一道题的操作数数量 4 operate_number = [] # 具体的操作数 5 pingfang_genhao = [] # 添加平方或根号 6 sin_cos_tan = [] # 添加三角函数 7 8 pingfang_genhao_num = 0 # 确保一定有平方或根号 9 sin_cos_tan_num = 0 # 确保一定有三角函数符号 10 11 # 在小学阶段,不会产生一个操作数的题目 12 if education == "小学": 13 while operate_number_num == 1: 14 operate_number_num = random.randint(1, 5) 15 16 # 如果是初中或高中阶段,则生成平方或根号 17 # 如果是高中阶段,则生成三角函数符号 18 for i in range(0, operate_number_num): 19 operate_number.append(random.randint(1, 100)) 20 21 if education != "小学": 22 suiji_num = random.randint(0, 2) 23 if suiji_num > 0: 24 pingfang_genhao_num += 1 25 pingfang_genhao.append(suiji_num) 26 27 if education == "高中": 28 suiji_num = random.randint(0, 2) 29 if suiji_num > 0: 30 pingfang_genhao_num += 1 31 sin_cos_tan.append(suiji_num) 32 33 # 如果初中阶段没有平方或根号的出现,则要加上一个 34 if pingfang_genhao_num == 0 and education == "初中": 35 add_pingfang_genhao = random.randint(0, operate_number_num - 1) 36 pingfang_genhao[add_pingfang_genhao] = random.randint(1, 2) 37 38 # 如果高中阶段没有三角函数符号的出现,则要加上一个 39 if sin_cos_tan_num == 0 and education == "高中": 40 add_sin_cos_tan = random.randint(0, operate_number_num - 1) 41 sin_cos_tan[add_sin_cos_tan] = random.randint(1, 3) 42 43 operate_symbol = [] # 加减乘除四个运算符号 44 kuohao_left = -1 # 左括号位置 45 kuohao_right = -1 # 右括号位置 46 47 # 下面决定左右括号的有无以及位置 48 # 如果操作数大于2,要随机生成是否使用括号 49 if operate_number_num > 2: 50 kuohao_left = random.randint(-1, operate_number_num - 2) 51 if kuohao_left == 0: 52 kuohao_right = random.randint(kuohao_left + 1, operate_number_num - 2) 53 elif kuohao_left > 0: 54 kuohao_right = random.randint(kuohao_left + 1, operate_number_num - 1) 55 56 # 生成符号,如果有三个数,就生成两个符号 57 for i in range(0, operate_number_num - 1): 58 operate_symbol.append(random.randint(1, 4)) 59 60 # 添加序号 61 str_one_question += str(serial_number + 1) + ". " 62 63 # 生成题目的主要环节,以数字为分界线,通过上面生成的符号信息,平方或根号,三角函数,括号信息 64 # 按顺序判断一个数字前有无根号,有无三角函数,有无左括号, 65 # 生成一个数字 66 # 按顺序判断一个数字后有无平方号,有无右括号 67 # 添加完数字,符号后,加入=?以及换行符,即成功生成一道题目 68 for i in range(0, operate_number_num): 69 70 if kuohao_left == i: 71 str_one_question += "(" 72 73 if education != "小学" and pingfang_genhao[i] == 1: 74 str_one_question += "√" 75 76 if education == "高中" and sin_cos_tan[i] != 0: 77 if sin_cos_tan[i] == 1: 78 str_one_question += "sin" 79 elif sin_cos_tan[i] == 2: 80 str_one_question += "cos" 81 else: 82 str_one_question += "tan" 83 84 str_one_question += str(operate_number[i]) 85 86 if education != "小学" and pingfang_genhao[i] == 2: 87 str_one_question += "^2" 88 89 if kuohao_right == i: 90 str_one_question += ")" 91 92 if i < operate_number_num - 1: 93 if operate_symbol[i] == 1: 94 str_one_question += "+" 95 elif operate_symbol[i] == 2: 96 str_one_question += "-" 97 elif operate_symbol[i] == 3: 98 str_one_question += "*" 99 else: 100 str_one_question += "/" 101 102 str_one_question += "=?\n" 103 104 str3 = "".join(str_one_question) 105 106 return str3
先使用一个列表来存放题目的每一个字符,然后使用join()函数将字符列表组合为字符串形式
根据教学阶段以及当前题目的题号结合random函数生成一道随机题目,题目前会加上题序。
5.check_duplicate(str_one_question) 题目查重
1 def check_duplicate(str_one_question): 2 location = GLOBAL_VAR_LOCATION + 'duplicate' + '.txt' 3 try: 4 if os.path.getsize(location) == 0: 5 return False 6 str_temp = str_one_question[3:] 7 file_duplicate = open(location, 'r') 8 for history_question in file_duplicate.readlines(): 9 if str(history_question) == str_temp: 10 file_duplicate.close() 11 return True 12 file_duplicate.close() 13 return False 14 15 except FileNotFoundError: 16 str_temp = str_one_question[3:] 17 file_duplicate = open(location, 'w') 18 file_duplicate.write(str_temp) 19 file_duplicate.close()
用for循环和.readlines函数相结合,读取查重文档duplicate.txt中的历史题目,然后让当前生成的题目与之比对
因为生成过来的题目str_one_question前面附带了题号,所以需要先把题号删去再进行比对
5.优缺点分析
优点
1.代码比较简洁,项目结构清楚,多用返回值和传参函数,较好的掌握面向对象的编程思想,便于以后的修改的使用
2.注释比较详细,基本讲解了各代码的具体实现以及变量的命名也符合规范
3.生成题目保存在指定文件夹,使用时比较顺畅
4.使用界面较好,用户使用时有提示信息,较为人性化
缺点
1.生成题目并不是完全随机,比如括号以及平方和根号只会加在一个位置,没有做到括号等符号的数量随机,生成的题目较为平常
6.总结
队友和我都使用了python语言进行编程,通过第一次个人项目互评,既让我熟悉了队友的编程风格,也让我学习到队友代码的优点,从而认识到自己代码的不足,虽然我的代码用比较巧妙的方法实现了相应功能,但我的代码封装性较弱,不利于日后的维护及使用,希望以后我能向队友学习,慢慢改进自己的代码风格.

浙公网安备 33010602011771号