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('>>>按隨機鍵退出!')

成品:

在這裏插入圖片描述

在這裏插入圖片描述

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