中小学数学卷子自动生成程序——结对编程项目分析

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语言进行编程,通过第一次个人项目互评,既让我熟悉了队友的编程风格,也让我学习到队友代码的优点,从而认识到自己代码的不足,虽然我的代码用比较巧妙的方法实现了相应功能,但我的代码封装性较弱,不利于日后的维护及使用,希望以后我能向队友学习,慢慢改进自己的代码风格.



 
posted @ 2021-09-27 21:23  微雨_KID  阅读(124)  评论(0)    收藏  举报