Python cho học sinh: tự làm app trắc nghiệm và flashcard ôn thi từ file CSV trong 45 phút, không cần cài thêm thư viện
Ôn thi bằng cách đọc lại vở nhiều lần thường không hiệu quả bằng tự kiểm tra mình. Bài này hướng dẫn bạn tự làm một app nhỏ bằng Python: nhập câu hỏi từ file CSV, app xáo câu hỏi và đáp án, chấm điểm, in lại câu sai để ôn lại; đổi sang chế độ flashcard thì lật từng thẻ và tự chấm. Không cần cài thêm thư viện, chỉ cần Python có sẵn.
Quyết định nhanh trước khi đọc tiếp: nếu bạn muốn luyện trắc nghiệm có chấm điểm, làm chế độ quiz. Nếu bạn muốn ôn nhanh theo kiểu nhớ – lật thẻ, làm chế độ flashcard. Cả hai đều dùng chung một file câu hỏi, nên bạn chỉ cần viết câu hỏi một lần.
Tóm tắt trong 30 giây
Làm gì: một app trắc nghiệm và flashcard ôn thi, đọc câu hỏi từ file cau-hoi.csv .
Thời gian: khoảng 45 phút, tính cả lúc viết 5 câu hỏi mẫu và chạy thử.
Trình độ: đã biết biến, vòng lặp, hàm và chạy file Python trong terminal, giống bài tool dọn file trước đó trong series này.
Cần gì: Python 3.10 trở lên, không cần cài thêm thư viện — code chỉ dùng csv , random và sys , là thư viện có sẵn của Python.
Kết quả: một file quiz.py chạy được bằng lệnh, có --seed để xáo giống nhau mỗi lần chạy, và --luu để ghi kết quả vào ket-qua.csv .
File câu hỏi: viết câu hỏi một lần, dùng được nhiều lần
Sơ đồ minh họa: câu hỏi sống trong file CSV, app chỉ đọc file đó — muốn thêm môn mới, bạn thêm dòng vào CSV, không cần sửa code.
Tạo file cau-hoi.csv với đúng header này, lưu dạng UTF-8 (mở bằng Excel hoặc Google Sheets thì chọn lưu UTF-8 khi xuất file):
cau_hoi,dap_an_dung,dap_an_sai_1,dap_an_sai_2,dap_an_sai_3
File mẫu có 5 câu Python cơ bản, dùng để chạy thử ngay:
cau_hoi,dap_an_dung,dap_an_sai_1,dap_an_sai_2,dap_an_sai_3 "Hàm nào in ra màn hình trong Python?",print,input,output,echo "Kiểu dữ liệu nào lưu được nhiều giá trị theo thứ tự?",list,dict,int,str "Lệnh nào lặp lại một khối lệnh với số lần xác định?",for,while-if,print,import "Đuôi file mã nguồn Python thường là gì?",.py,.txt,.docx,.html "Từ khóa nào định nghĩa một hàm trong Python?",def,func,define,fn
Mỗi dòng là một câu hỏi: cột đầu là câu hỏi, cột tiếp theo là đáp án đúng, ba cột sau là đáp án sai. Đáp án sai phải do chính bạn viết — và viết càng giống đáp án đúng thì bài luyện càng có tác dụng. Tool này không tự sinh câu hỏi, cũng không tự viết đáp án sai cho bạn.
Code đầy đủ của app
Tạo file quiz.py cùng thư mục với cau-hoi.csv , copy toàn bộ code dưới đây vào và lưu lại. Đây là code đã được chạy thử thật với Python 3.12.3, không phải code minh họa. Ảnh trong bài chỉ là sơ đồ minh họa, phần làm thật nằm ở code và lệnh bên dưới:
"""Quiz + flashcard tu file CSV — khong can cai them thu vien. Dinh dang cau-hoi.csv (UTF-8, co header): cau_hoi,dap_an_dung,dap_an_sai_1,dap_an_sai_2,dap_an_sai_3 Che do: python3 quiz.py cau-hoi.csv -> trac nghiem, xao dap an python3 quiz.py cau-hoi.csv --flashcard -> lat the, tu cham dung/sai python3 quiz.py cau-hoi.csv --seed 7 -> xao giong nhau moi lan chay Ket qua trac nghiem luu them vao ket-qua.csv (neu co --luu). """ import csv, random, sys from pathlib import Path def load(path): rows = [] with open(path, encoding="utf-8-sig", newline="") as f: for r in csv.DictReader(f): q = (r.get("cau_hoi") or "").strip() ok = (r.get("dap_an_dung") or "").strip() wrong = [ (r.get(k) or "").strip() for k in ("dap_an_sai_1","dap_an_sai_2","dap_an_sai_3")] wrong = [w for w in wrong if w] if q and ok and wrong: rows.append((q, ok, wrong)) if not rows: raise SystemExit("Khong doc duoc cau hoi hop le. Kiem tra header va file CSV.") return rows def quiz(rows, rng, save): score, wrong_list = 0, [] for i, (q, ok, wrong) in enumerate(rows, 1): opts = [ok] + wrong[:3] rng.shuffle(opts) print(f"\n{i}. {q}") for j, o in enumerate(opts): print(f" {chr(65+j)}. {o}") ans = input("Chon A/B/C/D (Enter = bo qua): ").strip().upper() idx = ord(ans) - 65 if len(ans) == 1 and ans in "ABCD" else -1 chosen = opts[idx] if 0 <= idx < len(opts) else None if chosen == ok: print(" Dung!") score += 1 else: print(f" Sai. Dap an dung: {ok}") wrong_list.append((q, ok)) print(f"\nKet qua: {score}/{len(rows)}") if wrong_list: print("\nOn lai cac cau sai:") for q, ok in wrong_list: print(f" - {q} -> {ok}") if save: with open("ket-qua.csv", "a", encoding="utf-8", newline="") as f: w = csv.writer(f); w.writerow([len(rows), score]) print("Da luu vao ket-qua.csv") return score def flashcard(rows, rng): score = 0 for i, (q, ok, _) in enumerate(rows, 1): print(f"\nThe {i}: {q}") input("Nhan Enter de lat the...") print(f" Dap an: {ok}") mark = input("Ban tra loi dung khong? (y/n): ").strip().lower() if mark == "y": score += 1 print(f"\nTu cham: {score}/{len(rows)} cau dung") def main(): if len(sys.argv) < 2: raise SystemExit("Dung: python3 quiz.py cau-hoi.csv [--flashcard] [--seed N] [--luu]") args = sys.argv[1:] path, mode, seed, save = args[0], "quiz", None, False if "--flashcard" in args: mode = "flashcard" if "--luu" in args: save = True if "--seed" in args: seed = int(args[args.index("--seed") + 1]) rng = random.Random(seed) rows = load(path) rng.shuffle(rows) if mode == "flashcard": flashcard(rows, rng) else: quiz(rows, rng, save) if __name__ == "__main__": main()
Code này làm gì, đọc theo từng hàm:
load đọc file CSV bằng csv.DictReader , lấy câu hỏi, đáp án đúng và tối đa 3 đáp án sai. Dòng nào thiếu câu hỏi, thiếu đáp án đúng hoặc không có đáp án sai nào sẽ bị bỏ qua. Nếu cuối cùng không còn câu hỏi hợp lệ nào, app dừng và báo “Khong doc duoc cau hoi hop le. Kiem tra header va file CSV.” — thay vì chạy âm thầm với 0 câu.
quiz xáo thứ tự đáp án mỗi câu, in đáp án theo A/B/C/D, chấm ngay sau mỗi câu, và cuối bài in lại toàn bộ câu sai kèm đáp án đúng để bạn ôn lại. Nhấn Enter mà không chọn gì được tính là bỏ qua câu đó.
flashcard in câu hỏi, chờ bạn nhấn Enter để lật thẻ xem đáp án, rồi hỏi bạn tự chấm đúng hay sai bằng y / n . Điểm flashcard là điểm tự chấm, nên chỉ có ý nghĩa khi bạn chấm thật.
main đọc lệnh từ terminal: file CSV, chế độ quiz hay flashcard, có dùng --seed để xáo giống nhau không, và có ghi kết quả bằng --luu không.
Chế độ 1: trắc nghiệm xáo đáp án, chấm điểm, in lại câu sai
Sơ đồ minh họa: đáp án đúng không nằm cố định ở một vị trí — mỗi lần chạy, đáp án được xáo lại.
Chạy lệnh sau, đứng ở thư mục chứa quiz.py và cau-hoi.csv :
python3 quiz.py cau-hoi.csv --seed 7
--seed 7 làm cho thứ tự câu hỏi và đáp án giống nhau mỗi lần chạy với cùng seed, để bạn kiểm tra lại kết quả hoặc làm lại cùng một đề. Không có --seed , mỗi lần chạy sẽ xáo khác nhau.
Với file mẫu và đáp án nhập vào lần lượt là A, B, C, D, A, kết quả chạy thử thật là 2/5, và cuối bài app in lại 3 câu sai:
1. Từ khóa nào định nghĩa một hàm trong Python? A. func B. fn C. define D. def Chon A/B/C/D (Enter = bo qua): Sai. Dap an dung: def 2. Hàm nào in ra màn hình trong Python? A. input B. print C. echo D. output Chon A/B/C/D (Enter = bo qua): Dung! 3. Đuôi file mã nguồn Python thường là gì? A. .html B. .docx C. .py D. .txt Chon A/B/C/D (Enter = bo qua): Dung! 4. Kiểu dữ liệu nào lưu được nhiều giá trị theo thứ tự? A. int B. list C. dict D. str Chon A/B/C/D (Enter = bo qua): Sai. Dap an dung: list 5. Lệnh nào lặp lại một khối lệnh với số lần xác định? A. print B. import C. for D. while-if Chon A/B/C/D (Enter = bo qua): Sai. Dap an dung: for Ket qua: 2/5 On lai cac cau sai: - Từ khóa nào định nghĩa một hàm trong Python? -> def - Kiểu dữ liệu nào lưu được nhiều giá trị theo thứ tự? -> list - Lệnh nào lặp lại một khối lệnh với số lần xác định? -> for
Muốn lưu kết quả lại để theo dõi tiến bộ, thêm --luu :
python3 quiz.py cau-hoi.csv --seed 7 --luu
App sẽ ghi thêm một dòng vào ket-qua.csv , ví dụ 5,2 nghĩa là 5 câu, đúng 2 câu, và in ra “Da luu vao ket-qua.csv”. File này ghi thêm (append), không ghi đè, nên bạn có thể chạy nhiều lần và xem điểm có tăng lên không.
Chế độ 2: flashcard lật thẻ, tự chấm
Sơ đồ minh họa: flashcard chỉ có câu hỏi ở mặt trước và đáp án ở mặt sau — bạn phải tự nhớ trước khi lật.
python3 quiz.py cau-hoi.csv --flashcard
Mỗi thẻ hiện câu hỏi, bạn tự trả lời trong đầu, nhấn Enter để lật thẻ xem đáp án, rồi nhập y nếu bạn trả lời đúng, n nếu sai. Cuối bài app in ra điểm tự chấm, ví dụ “Tu cham: 3/5 cau dung” trong lần chạy thử với đáp án tự chấm y, y, n, y, n.
Chế độ này hợp để ôn nhanh trước giờ kiểm tra, vì bạn không bị phân tâm bởi các đáp án sai — bạn phải tự nhớ ra đáp án. Nhược điểm là điểm số phụ thuộc vào sự trung thực của bạn, nên đừng dùng điểm flashcard để tự đánh lừa mình.
Khi gặp lỗi, kiểm tra theo thứ tự này
App báo “Khong doc duoc cau hoi hop le. Kiem tra header va file CSV.”: kiểm tra header phải đúng chính xác cau_hoi,dap_an_dung,dap_an_sai_1,dap_an_sai_2,dap_an_sai_3 , không thừa khoảng trắng, không thiếu cột. Mở file CSV bằng Excel hoặc Google Sheets rồi lưu lại dạng UTF-8 cũng giúp tránh lỗi font chữ.
App báo không tìm thấy file: bạn đang đứng sai thư mục trong terminal. Gõ lại lệnh từ thư mục chứa quiz.py và cau-hoi.csv .
Câu hỏi có dấu phẩy bị tách làm hai cột: đặt câu hỏi hoặc đáp án chứa dấu phẩy trong dấu ngoặc kép, giống như các câu mẫu trong file trên. Đó là quy tắc của CSV, không phải lỗi của app.
Muốn làm lại cùng một đề nhưng thứ tự khác nhau: bỏ --seed đi, hoặc đổi sang một số khác, ví dụ --seed 42 .
Muốn thêm môn mới: tạo file CSV mới với cùng header, ví dụ tieng-anh.csv , rồi chạy python3 quiz.py tieng-anh.csv . Không cần sửa code.
Giới hạn cần nói rõ
Đáp án sai do người làm tự viết, tool không tự sinh câu hỏi hay đáp án sai. Nếu đáp án sai viết quá dễ đoán, bài luyện sẽ không có tác dụng — lỗi này nằm ở bộ câu hỏi, không nằm ở code.
File CSV sai header sẽ báo lỗi và dừng lại, chứ không chạy âm thầm với dữ liệu sai. Đó là hành vi cố tình, để bạn sửa file thay vì nhận một kết quả chấm sai.
App này chạy trong terminal, chưa có giao diện bấm nút, chưa lưu được lịch sử ôn tập theo ngày. Nếu bạn muốn bước tiếp theo, hướng tự nhiên là thêm giao diện Tkinter, hoặc lưu điểm theo ngày để vẽ biểu đồ tiến bộ — cả hai đều dùng lại đúng file CSV và hàm load như bài này.
Kết luận: viết câu hỏi mới là phần học thật
Phần code của app này không dài, nhưng phần có giá trị nhất không nằm trong code — nó nằm ở lúc bạn tự viết câu hỏi và đáp án sai. Viết được một đáp án sai đủ giống đáp án đúng nghĩa là bạn đã hiểu chỗ dễ nhầm của chính mình. Hãy bắt đầu bằng 5 câu mẫu, rồi thay bằng 10 câu của môn bạn đang ôn, chạy lại với --seed khác nhau, và xem điểm của bạn có tăng lên sau mỗi lần ôn lại các câu sai hay không.