Python:通过编虐娃数学题,学习多线程技术

前言:

最近因大家都知道的原因,只能在家自学,之前打印的数学考卷存货不多了,所以想在电脑上编一个来代替之前的数学考卷版本《Python:大班数学自动生成器》。
想了2个版本:第一个是利用pyqt5来做个GUI版本,但是苦于对GUI不熟,所以一直没成功;第二个版本则比较简单,使用了CMD的窗口,但是因为我比较虐娃,所以加了1分钟的倒计时,时间到了但还没做的话就本道题按0分处理。要加这个功能就必须用到多线程和线程关闭的技巧了。由于我之前也没怎么研究过多线程,所以正好虐娃前先自虐,还好最后成功了。

注:目前线程关闭的方法抄了网上的,但是如果一个线程用了input的话,即使关闭了该线程也必须输入内容才能继续,为了解决这个问题,只能在另一个线程里再次加入了input的语句,提示“回车两次继续”,如果有人有更好的解决办法请留言!

代码:

import random
import traceback
import threading
import inspect
import ctypes
import time

class MyThread(threading.Thread):
    def __init__(self,func,args=()):
        super(MyThread,self).__init__()
        self.func = func
        self.args = args
    def run(self):
        self.result = self.func(*self.args)
    def get_result(self):
        try:
            return self.result
        except Exception:
            return None

def _async_raise(tid, exctype):
    """raises the exception, performs cleanup if needed"""
    tid = ctypes.c_long(tid)
    if not inspect.isclass(exctype):
        exctype = type(exctype)
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
    if res == 0:
        raise ValueError("invalid thread id")
    elif res != 1:
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
        raise SystemError("PyThreadState_SetAsyncExc failed")

def stop_thread(thread):
    _async_raise(thread.ident, SystemExit)

def math_question(max_question_num, min_range, max_range, min_answer, max_answer):
    '''数学题目生成器'''
    question_list = []
    answer_list = []
    question_num = 0
    while True:
        if question_num >= max_question_num :
            break
        else:
            question = str(random.randint(min_range, max_range))  # 这里选择单个数字的取数范围
            j = int(max_num)
            while j - 1:
                question = question + random.choice(['+', '-'])  # 这里选择运算符的范围
                question = question + str(random.randint(min_range, max_range))
                j = j - 1
            answer = eval(question)
            if min_answer <= answer <= max_answer:  # 这里选择答案的取数范围
                this_question = question + '='
                if '*' in this_question:
                    this_question = this_question.replace('*', '×')
                if this_question not in question_list:
                    question_list.append(this_question)
                    answer_list.append(answer)
                    question_num = question_num + 1
    return question_list, answer_list

def custom_mode():
    '''自定义模式'''
    # 1、考题数量
    max_question_num = input('>>>请输入考题数量(如20):(直接回车默认20)')
    if not max_question_num:
        max_question_num = 20
    # 2、单个数字的取值范围
    num_range = input('>>>请输入单个数字的取值范围(如1,20):(直接回车默认1-20)')
    if not num_range:
        min_range, max_range = 1, 20
    else:
        min_range, max_range = num_range.split(',')
    # 3、答案的取值范围
    answer_range = input('>>>请输入答案的取值范围(如1,20):(直接回车默认1-20)')
    if not answer_range:
        min_answer, max_answer = 1, 20
    else:
        min_answer, max_answer = answer_range.split(',')
    # 4、单道题目中数字的最大数量(比如1+2+3就是3个数字)
    max_num = input('>>>请输入单道题目中数字的最大数量(如2):(直接回车默认2)')
    if not max_num:
        max_num = 2
    return max_question_num, min_range, max_range, min_answer, max_answer, max_num

def mode_choice():
    '''模式选择'''
    mode_choice = int(input('>>>请选择模式(1:小班,2:中班,3:大班,4:小学,5:自定义):'))
    if mode_choice == 1:
        max_question_num, min_range, max_range, min_answer, max_answer, max_num = 10, 0, 9, 0, 9, 2
    elif mode_choice == 2:
        max_question_num, min_range, max_range, min_answer, max_answer, max_num = 20, 0, 10, 0, 20, 2
    elif mode_choice == 3:
        max_question_num, min_range, max_range, min_answer, max_answer, max_num = 50, 1, 20, 1, 20, 2
    elif mode_choice == 4:
        max_question_num, min_range, max_range, min_answer, max_answer, max_num = 100, 1, 100, 1, 100, 2
    elif mode_choice == 5:
        max_question_num, min_range, max_range, min_answer, max_answer, max_num = custom_mode()
    max_question_num, min_range, max_range, min_answer, max_answer, max_num = int(max_question_num), int(min_range), int(max_range), int(min_answer), int(max_answer), int(max_num)
    return max_question_num, min_range, max_range, min_answer, max_answer, max_num

def make_your_answer():
    '''输入答案'''
    your_answer = input('>>>请输入答案:')
    try:
        your_answer = int(your_answer)
        return your_answer
    except:
        print('>>>请输入整数!')
        return make_your_answer()

def check_answer(answer, your_answer, score):
    if your_answer == answer:
        print('>>>恭喜你,答对啦!真棒!')
        print('-' * 100)
        score += 1
        return score
    else:
        print('>>>真可惜,答错啦!')
        print('>>>"{}"的答案是:{}'.format(question, answer))
        print('-' * 100)
        return score

def make_answer():
    '''答题'''
    your_answer = make_your_answer()
    stop_thread(thread2) #关闭线程2,也就是倒计时
    return your_answer

def countdown():
    '''倒计时'''
    for i in range(0, 60):
        time.sleep(1)
    print('\n>>>时间到!')
    stop_thread(thread1) #关闭线程1,也就是答题
    print('>>>按2次回车键继续!')
    a = input('>>>') #这里也是无奈之举
    return 0

if __name__ =="__main__":
    try:
        max_question_num, min_range, max_range, min_answer, max_answer, max_num = mode_choice()
        # print(sign_list, min_range, max_range, min_answer, max_answer)
        question_list, answer_list = math_question(max_question_num, min_range, max_range, min_answer, max_answer)
        score = 0 #答对的
        overtime = 0 #超时的
        n = 1
        for question, answer in zip(question_list, answer_list):
            print('>>>第{}题是:{}'.format(n, question))
            n += 1
            thread1 = MyThread(make_answer)
            thread2 = MyThread(countdown)
            thread1.start()
            thread2.start()
            thread2.join()
            your_answer = thread1.get_result()
            if your_answer is None: #如果超时的话,返回的就是None
                overtime += 1
            # print(your_answer)
            score = check_answer(answer, your_answer, score)
        wrong_score = max_question_num - score - overtime
        accuracy = score / max_question_num
        if accuracy < 0.6: evalute = 'D'
        elif 0.6<= accuracy < 0.8: evalute = 'C'
        elif 0.8<= accuracy <0.9: evalute = 'B'
        elif 0.9<= accuracy < 1: evalute = 'A'
        elif accuracy == 1: evalute = '100分'
        accuracy = format(accuracy, '.0%')
        print('>>>考试完成!\n题目总数:{}\n答对:{}\n答错:{}\n未答:{}\n准确率:{}\n评价:{}'.format(max_question_num, score, wrong_score, overtime, accuracy, evalute))
    except:
        traceback.print_exc()
    finally:
        a = input('>>>按随机键退出!')

成品:

在这里插入图片描述

在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章