Bài tập: phân loại điểm
Bạn sẽ học được
- Ghép f-string, xử lý chuỗi, ép kiểu và điều kiện thành một chương trình hoàn chỉnh.
- Viết bảng xếp loại điểm với các mốc 90/80/70/50.
- Phân biệt "điểm không hợp lệ" với "điểm 0".
- Xử lý đầu vào là chuỗi, số thực,
None, chuỗi rỗng. - Tự kiểm tra bằng một bộ ca thử thay vì gõ tay từng lần.
Đề bài
Viết chương trình nhận tên và điểm (có thể là chuỗi từ bàn phím), in ra xếp loại:
| Điểm | Xếp loại |
|---|---|
| Từ 90 đến 100 | A |
| Từ 80 đến dưới 90 | B |
| Từ 70 đến dưới 80 | C |
| Từ 50 đến dưới 70 | D |
| Dưới 50 | F |
Điểm ngoài khoảng 0–100 hoặc không đọc được thành số phải báo "điểm không hợp lệ". Tên rỗng hiển thị là "bạn".
Khái niệm cốt lõi
Tách bài toán thành ba hàm nhỏ, mỗi hàm một việc:
def xep_loai(diem):
if not 0 <= diem <= 100:
return "Điểm không hợp lệ"
if diem >= 90:
return "A"
if diem >= 80:
return "B"
if diem >= 70:
return "C"
if diem >= 50:
return "D"
return "F"Sau khi đã loại điểm ngoài khoảng, các nhánh chỉ cần kiểm tra cận dưới vì nhánh trước đã chặn cận trên. if diem >= 80 đứng sau nhánh >= 90, nên nó chỉ nhận điểm dưới 90. Đây là cách viết "thác nước" — điều kiện hẹp nhất trước, rộng dần.
Có thể dùng elif thay cho các if liền nhau; kết quả giống hệt vì mỗi nhánh đều return. Dùng if liền khi các nhánh độc lập và thoát sớm; dùng elif khi muốn nhấn mạnh "chỉ một nhánh chạy".
Kiểm tra thứ tự bằng bảng:
for diem in [95, 90, 85, 80, 72, 70, 50, 49, 100, 0, -5, 105]:
print(diem, xep_loai(diem))95 A
90 A
85 B
80 B
72 C
70 C
50 D
49 F
100 A
0 F
-5 Điểm không hợp lệ
105 Điểm không hợp lệHai mốc đáng chú ý: 90 là A (điều kiện >= 90), 49 là F (không thoả >= 50), 0 là F hợp lệ chứ không phải không hợp lệ. Nếu viết if not diem: thay cho kiểm tra khoảng, điểm 0 sẽ bị báo sai — đây chính là cái bẫy truthiness của bài trước.
Đọc điểm từ chuỗi
input() trả chuỗi, và dữ liệu thật có thể là "85", "89.5", " 70 ", "abc", "", thậm chí None. Hàm đọc phải trả None khi không đọc được, để phần sau phân biệt "không đọc được" với "đọc được số 0":
def doc_diem(raw):
try:
diem = float(raw)
except (TypeError, ValueError):
return None
return diem
print(doc_diem("85"), doc_diem("89.5"), doc_diem(" 70 "))
print(doc_diem("abc"), doc_diem(""), doc_diem(None))85.0 89.5 70.0
None None NoneVì sao bắt cả TypeError:
float("abc")némValueError.float("")némValueError.float(None)némTypeError.
Nếu chỉ bắt ValueError, ca None sẽ làm chương trình dừng. Bắt cả hai là cách viết phòng thủ cho dữ liệu đến từ ngoài.
float() tự bỏ khoảng trắng hai đầu, nên " 70 " đọc được mà không cần .strip(). Nó cũng nhận "1e2" (bằng 100) và "85,5" sẽ lỗi vì dấu phẩy thập phân kiểu Việt không phải cú pháp Python — nếu dữ liệu dùng dấu phẩy, phải replace(",", ".") trước.
Ghép báo cáo
def bao_cao(name, raw):
name = name.strip() or "bạn"
diem = doc_diem(raw)
if diem is None:
return f"{name}: điểm không hợp lệ"
return f"{name}: {diem:g} → {xep_loai(diem)}"name.strip() or "bạn" xử lý tên rỗng hoặc toàn khoảng trắng. {diem:g} in số gọn: 85.0 thành 85, 89.5 giữ nguyên. Nếu dùng {diem} mặc định, kết quả sẽ là 85.0.
Chạy bộ ca thử:
cases = [
("An", "85"),
("Bình", "89.5"),
("Chi", "abc"),
("Dũng", ""),
("Em", " 70 "),
("Phúc", None),
("Giang", "100"),
(" ", "49"),
]
for name, raw in cases:
print(bao_cao(name, raw))An: 85 → B
Bình: 89.5 → B
Chi: điểm không hợp lệ
Dũng: điểm không hợp lệ
Em: 70 → C
Phúc: điểm không hợp lệ
Giang: 100 → A
bạn: 49 → FLưu ý "89.5" là B chứ không phải A: mốc A là 90, không phải 89. Đây là dạng lỗi lệch một đơn vị rất dễ xảy ra khi dùng > thay >=.
Bọc phần nhập bàn phím ở ngoài cùng để hàm vẫn kiểm thử được:
if __name__ == "__main__":
name = input("Tên: ")
raw = input("Điểm: ")
print(bao_cao(name, raw))Tự kiểm tra
Trả lời trước khi xem đáp án:
- Điểm
90thuộc loại nào?89.9thuộc loại nào? - Vì sao phải kiểm tra khoảng
0 <= diem <= 100trước các nhánh xếp loại? float(None)ném lỗi gì? Vì sao phải bắt cảTypeError?doc_diemtrảNonethay vì0khi lỗi — lợi ích là gì?- Vì sao
{diem:g}tốt hơn{diem}trong báo cáo? - Nếu bỏ nhánh kiểm tra khoảng, điểm
105sẽ ra loại gì?
Đáp án:
90là A;89.9là B.- Để điểm âm hoặc trên 100 không bị xếp vào A/F như điểm hợp lệ.
TypeError;float("abc")làValueError, cònNonesai kiểu hoàn toàn.Nonenghĩa là "không có dữ liệu", phân biệt được với điểm0hợp lệ.:gbỏ phần.0thừa, giữ89.5nguyên vẹn.105 >= 90nên sẽ ra A — sai hoàn toàn.
Thực hành thêm
- Đổi bảng xếp loại sang thang 10 (9/8/7/5).
- Nhận thêm hệ số chuyên cần và hạ một bậc nếu dưới 0.8.
- Dùng
matchthayif/elifcho các mốc điểm và so sánh độ đọc hiểu. - Thêm ca thử
"-5","105","1e2","85,5"và dự đoán kết quả trước khi chạy. - In báo cáo cho cả lớp từ danh sách
(ten, diem)và đếm số lượng mỗi loại bằng dict.
Lỗi thường gặp
| Triệu chứng | Nguyên nhân | Cách sửa |
|---|---|---|
Điểm 0 bị báo không hợp lệ | Dùng if not diem | Kiểm tra is None và khoảng |
105 ra loại A | Không chặn cận trên | Kiểm tra 0 <= diem <= 100 |
89.5 ra A | Dùng > thay >= hoặc sai mốc | Rà lại bảng mốc |
Chương trình dừng ở None | Chỉ bắt ValueError | Bắt thêm TypeError |
85.0 hiện trong báo cáo | Thiếu format spec | {diem:g} |
"85,5" lỗi | Dấu phẩy thập phân | replace(",", ".") trước |
| Tên rỗng hiện trống | Không xử lý chuỗi rỗng | name.strip() or "bạn" |
Tóm tắt
- Xếp loại bằng thác nước điều kiện: chặn khoảng trước, rồi so cận dưới.
0là điểm hợp lệ;Nonemới là "không đọc được".float()némValueErrorhoặcTypeError— bắt cả hai.- Tách hàm nhỏ để chạy được bộ ca thử không cần bàn phím.
:ggiữ báo cáo gọn;>=quyết định đúng các mốc biên.
Use ← → to move between lessons