Bài 9 · Vận dụng · 20 phút· Cập nhật 11/06/2026
Lỗi & ngoại lệ trong Python
Biên soạn bởi Nguyễn Anh Tuấn
Xử lý lỗi & ngoại lệ (exception) trong Python: try/except/finally, raise, đọc traceback, bắt lỗi cụ thể và triết lý EAFP.
Khi “có chuyện” lúc chạy, Python tạo một object lỗi và “ném” (raise) nó. Vài loại hay gặp: ValueError (giá trị sai), TypeError (sai kiểu), KeyError (key không có), ZeroDivisionError, IndexError, FileNotFoundError… Không bắt thì chương trình dừng và in traceback:
loi.py
int("abc") Kết quả khi chạy
Traceback (most recent call last):
File "loi.py", line 1, in <module>
int("abc")
ValueError: invalid literal for int() with base 10: 'abc'- ▸Đọc traceback TỪ DƯỚI LÊN: dòng cuối = loại lỗi + thông điệp.
- ▸Mỗi loại lỗi là một kiểu riêng (ValueError, KeyError…), đều thuộc Exception.
- ▸Lỗi không bắt sẽ dừng chương trình - nên ta cần cách XỬ LÝ chúng.
Đặt phần “có thể lỗi” trong try; nếu lỗi xảy ra và khớp một except, Python nhảy tới đó. finally LUÔN chạy. Bấm Tiếp → với ba tình huống để thấy luồng nhảy:
- ▸try lỗi ở đâu thì DỪNG ngay, nhảy tới except khớp loại lỗi.
- ▸except không khớp → lỗi LAN RA ngoài (nhưng finally vẫn chạy trước).
- ▸finally luôn chạy - hợp để dọn dẹp (đóng tệp, kết nối).
Khi hàm của bạn gặp dữ liệu không hợp lệ, hãy raise một lỗi rõ nghĩa thay vì âm thầm trả sai:
raise.py
def dat_tuoi(t):
if t < 0:
raise ValueError("tuoi khong the am")
return t
try:
dat_tuoi(-5)
except ValueError as e:
print("Loi:", e) Kết quả khi chạy
Loi: tuoi khong the am
- ▸raise LoaiLoi("thông điệp") để báo điều kiện sai cho người gọi.
- ▸except ... as e: bắt object lỗi, e chứa thông điệp.
- ▸Báo lỗi sớm & rõ tốt hơn để bug lan đi âm thầm.
Dạng đầy đủ còn có else (chạy khi KHÔNG có lỗi), và bạn nên bắt đúng loại lỗi - đừng nuốt tất cả:
full.py
def doc_so(s):
try:
n = int(s) # co the loi
except ValueError:
return None # bat CU THE loai nay
else:
return n * 2 # chi chay khi try KHONG loi
finally:
print("xong") # LUON chay
print(doc_so("5")) # -> xong, roi 10
print(doc_so("x")) # -> xong, roi None Kết quả khi chạy
xong 10 xong None
bắt cụ thể: SAI → ĐÚNG
# SAI: nuot MOI loi (ke ca go sai ten bien -> bug an)
try:
kq = tinh()
except:
kq = 0
# DUNG: bat dung loai ban biet xu ly
try:
kq = tinh()
except (ValueError, ZeroDivisionError): # mot tuple nhieu loai
kq = 0 - ▸else: chạy khi try KHÔNG lỗi (tách phần “lỡ lỗi” khỏi phần “chạy tiếp khi ổn”).
- ▸except (A, B): bắt nhiều loại trong một nhánh; nhiều except cho xử lý khác nhau.
- ▸ĐỪNG dùng except: trống - nó nuốt cả lỗi bạn không ngờ (NameError…) → bug ẩn.
Bạn tạo được loại lỗi riêng bằng cách kế thừa Exception (hoặc một loại cụ thể) - giúp người gọi bắt đúng tình huống nghiệp vụ:
custom.py
class SoDuKhongDu(ValueError): # loi RIENG, ke thua ValueError
pass
def rut(sodu, tien):
if tien > sodu:
raise SoDuKhongDu(f"thieu {tien - sodu}")
return sodu - tien
try:
rut(100, 150)
except SoDuKhongDu as e: # bat dung loai nghiep vu
print("Loi:", e) Kết quả khi chạy
Loi: thieu 50
“raise ... from e”: giữ nguyên nhân gốc
Có hai lối nghĩ. LBYL (“nhìn trước khi nhảy”): kiểm tra trước rồi mới làm. EAFP (“xin lỗi dễ hơn xin phép”): cứ thử, sai thì bắt. Python ưa EAFP - thường gọn và an toàn hơn:
eafp.py
diem = {"Lan": 9}
# LBYL: kiem tra truoc
if "Nam" in diem:
print(diem["Nam"])
else:
print("chua co Nam")
# EAFP: cu thu, sai thi bat (Pythonic)
try:
print(diem["Nam"])
except KeyError:
print("chua co Nam") Kết quả khi chạy
chua co Nam chua co Nam
Vì sao Python ưa EAFP
Câu hỏi thường gặp
Là một sự kiện “có chuyện” lúc chạy - chia cho 0, key không có, ép kiểu hỏng… Python tạo một OBJECT lỗi (vd ZeroDivisionError) và “ném” (raise) nó. Nếu không ai bắt, chương trình dừng và in TRACEBACK.
Đọc TỪ DƯỚI LÊN: dòng cuối cho biết LOẠI lỗi và thông điệp (vd ValueError: invalid literal...). Các dòng trên cho biết lỗi xảy ra ở tệp/dòng nào và chuỗi lời gọi dẫn tới đó.
finally LUÔN chạy - dù try thành công, bị bắt, hay lỗi văng ra ngoài. Dùng để DỌN DẸP chắc chắn: đóng tệp, đóng kết nối, giải phóng tài nguyên. (Với tệp, câu lệnh with còn gọn hơn - học ở khoá nâng cao.)
KHÔNG. except: hay except Exception: bắt MỌI lỗi sẽ “nuốt” cả lỗi bạn không ngờ (gõ sai tên biến, hết bộ nhớ…), khiến bug ẩn đi. Hãy bắt CỤ THỂ loại lỗi bạn biết cách xử lý (except KeyError, except ValueError…).
Tick những điều em tự tin làm được. Càng lên cao, em càng hiểu sâu.
Trả lời vài câu để chắc rằng em đã nắm bài.
Đọc một traceback thì nên đọc theo hướng nào để biết loại lỗi?
- 1
Đọc traceback
Chạy
int("abc"). Loại lỗi gì, thông điệp ra sao (dòng cuối traceback)?Hoàn thành khi:
ValueError: invalid literal for int() with base 10: 'abc'. - 2
Bắt lỗi chia 0
Viết hàm
chia(a, b)dùngtry/excepttrả "Khong chia cho 0" khib = 0.Hoàn thành khi:
try: return a / b; except ZeroDivisionError: return "Khong chia cho 0". - 3
finally luôn chạy
Dùng công cụ Bước 2: với
chia(10, "x"),finallycó chạy không trước khi lỗi văng ra?Hoàn thành khi: Có - "Xong." in ra trước, rồi
TypeErrormới lan ra ngoài. - 4
Tự ném lỗi
Viết
dat_tuoi(t): nếut < 0thìraise ValueError("tuoi khong the am").Hoàn thành khi:
raise ValueError(...)khit < 0; ngược lạireturn t. - 5
EAFP
Cho
diem = {"Lan": 9}. Lấy điểm "Nam" theo kiểu EAFP, in "chua co" nếu không có.Hoàn thành khi:
try: print(diem["Nam"]); except KeyError: print("chua co"). (Hoặcdiem.get("Nam", "chua co").) - 6
Bắt cụ thể
Vì sao
except:(trống) là thói quen xấu? Nêu một bug nó có thể che giấu.Hoàn thành khi: Nó nuốt mọi lỗi - kể cả gõ sai tên biến (
NameError) - làm bug ẩn, khó tìm.