Lỗi thường gặp
Bạn sẽ học được
- Đọc traceback để tìm đúng dòng gây lỗi.
- Nhận diện 6 exception hay gặp nhất khi xử lý dữ liệu.
- Bắt lỗi đúng cách:
try/except/else/finally, bắt cụ thể, không nuốt lỗi. - Chủ động ném lỗi với
raisevà tạo exception riêng. - Hai phong cách kiểm tra: EAFP và LBYL.
Khái niệm cốt lõi
Python báo lỗi bằng exception. Khi một exception không được bắt, chương trình dừng và in traceback — danh sách lời gọi từ ngoài vào trong, dòng cuối cùng là nguyên nhân:
Traceback (most recent call last):
File "bao_cao.py", line 12, in <module>
print(tong[cua_hang])
KeyError: 'b'Đọc traceback từ dưới lên: dòng cuối cho biết loại lỗi và thông điệp; ngay trên là dòng code gây lỗi; cao hơn là các lời gọi dẫn tới đó. Dòng cuối là thứ bạn cần trước tiên.
Exception cũng là một đối tượng có kiểu (class). Tên kiểu chính là phần trước dấu hai chấm: KeyError, ValueError, TypeError…
Sáu lỗi hay gặp nhất
Bảng dưới đây là sáu exception chiếm phần lớn lỗi khi mới xử lý dữ liệu:
| Exception | Khi nào | Ví dụ thông điệp |
|---|---|---|
KeyError | Truy cập khóa không có trong dict | KeyError: 'b' |
IndexError | Chỉ số vượt độ dài list | IndexError: list index out of range |
ValueError | Đúng kiểu nhưng sai giá trị | ValueError: invalid literal for int() with base 10: '12a' |
TypeError | Sai kiểu dữ liệu | TypeError: can only concatenate str (not "int") to str |
AttributeError | Gọi phương thức không tồn tại | AttributeError: 'str' object has no attribute 'append' |
FileNotFoundError | Mở file không tồn tại | FileNotFoundError: [Errno 2] No such file or directory: 'khong_ton_tai.csv' |
Ví dụ từng lỗi:
tong = {"a": 100, "c": 250}
print(tong["b"])KeyError: 'b'rows = ["2026-01", "2026-02"]
print(rows[5])IndexError: list index out of rangeint("12a")ValueError: invalid literal for int() with base 10: '12a'"năm " + 2026TypeError: can only concatenate str (not "int") to strten = "An"
ten.append("!")AttributeError: 'str' object has no attribute 'append'open("khong_ton_tai.csv")FileNotFoundError: [Errno 2] No such file or directory: 'khong_ton_tai.csv'Phân biệt quan trọng: ValueError là "đúng kiểu, sai nội dung" — int("12a") là chuỗi đúng kiểu nhưng không phải số. TypeError là "sai kiểu hoàn toàn" — cộng chuỗi với số, gọi .append trên chuỗi.
Bắt lỗi
Cú pháp đầy đủ gồm bốn khối:
def doc_so(raw):
try:
value = int(raw)
except ValueError:
print(f"'{raw}' không phải số nguyên")
return None
else:
print("Đọc thành công")
return value
finally:
print("Kết thúc lần đọc")
print(doc_so("42"))
print(doc_so("12a"))Đọc thành công
Kết thúc lần đọc
42
'12a' không phải số nguyên
Kết thúc lần đọc
NoneThứ tự chạy:
try: đoạn có thể lỗi.except: chỉ chạy khi có lỗi tương ứng.else: chỉ chạy khitrykhông lỗi — đặt phần "việc tiếp theo" ở đây để không bắt nhầm lỗi của chính nó.finally: luôn chạy, kể cả khi có lỗi hay córeturn— dùng để đóng file, giải phóng tài nguyên.
Bắt nhiều loại:
def to_int(raw):
try:
return int(raw.replace(",", ""))
except (TypeError, ValueError):
return 0
print(to_int("1,200"), to_int(None), to_int("abc"))1200 0 0Ba sai lầm khi bắt lỗi
1. Bắt quá rộng. except Exception: hoặc except: bắt mọi thứ, kể cả lỗi lập trình như gõ sai tên biến. Dùng except ValueError: — chỉ loại bạn thực sự xử lý được.
2. Nuốt lỗi im lặng. except ValueError: pass làm dữ liệu sai biến mất không dấu vết; báo cáo thiếu dòng mà không ai biết. Nên ghi log hoặc đếm số dòng lỗi:
loi = 0
for raw in ["10", "abc", "20"]:
try:
value = int(raw)
except ValueError:
loi += 1
print("Số dòng lỗi:", loi)Số dòng lỗi: 13. Bọc cả khối lớn. Chỉ đặt trong try đúng dòng có thể lỗi. Bọc cả hàm 20 dòng khiến bạn không biết lỗi đến từ đâu và vô tình bắt lỗi của đoạn không liên quan.
Chủ động ném lỗi
Khi dữ liệu đầu vào không hợp lệ, ném lỗi rõ ràng tốt hơn để lỗi lan xuống chỗ khó hiểu:
def tinh_trung_binh(diem):
if not diem:
raise ValueError("Danh sách điểm rỗng")
return sum(diem) / len(diem)
try:
print(tinh_trung_binh([]))
except ValueError as e:
print("Không tính được:", e)Không tính được: Danh sách điểm rỗngraise không có đối số trong khối except sẽ ném lại lỗi đang xử lý — hữu ích khi muốn ghi log rồi vẫn báo lỗi cho nơi gọi:
try:
...
except ValueError:
print("Ghi log: dữ liệu lỗi")
raiseVới ứng dụng lớn hơn, định nghĩa exception riêng để nơi gọi phân biệt được lỗi của mình với lỗi thư viện:
class DuLieuLoi(ValueError):
passKế thừa từ lớp phù hợp (ValueError ở đây) giúp code cũ vẫn bắt được.
EAFP và LBYL
Hai phong cách kiểm tra:
| Phong cách | Ý nghĩa | Ví dụ |
|---|---|---|
| LBYL | Look Before You Leap — kiểm tra trước | if "b" in tong: |
| EAFP | Easier to Ask Forgiveness — thử rồi bắt lỗi | try: tong["b"] |
tong = {"a": 100}
if "b" in tong: # LBYL
print(tong["b"])
else:
print("chưa có")
try: # EAFP
print(tong["b"])
except KeyError:
print("chưa có")chưa có
chưa cóPython nghiêng về EAFP ở nơi có race condition hoặc kiểm tra tốn kém (file có thể bị xóa giữa lúc kiểm tra và lúc mở). Nhưng với lỗi dữ liệu hàng loạt trong CSV, LBYL kèm đếm lỗi thường dễ đọc hơn.
Với dict, cách gọn nhất là get — không cần cả hai khối trên:
print(tong.get("b", "chưa có"))Gỡ lỗi nhanh
- Đọc dòng cuối traceback trước, rồi tới dòng code được chỉ.
- In biến ngay trước dòng lỗi:
print(type(row), row). repr()cho thấy khoảng trắng và ký tự ẩn:repr(row["so_luong"])→' 2'— một khoảng trắng thừa là đủ làmint()lỗi.- Kiểm tra
len()trước khi lấy chỉ số.
Thực hành
- Cố ý gây ra cả sáu lỗi trong bảng và đọc traceback từng lần.
- Viết
doc_sovới đủtry/except/else/finally, in ra từng nhánh được chạy. - Thử
except (TypeError, ValueError)vớiNone,"abc","1,200". - Đếm số dòng lỗi thay vì
passim lặng. - Viết hàm ném
ValueErrorkhi danh sách rỗng. - So sánh LBYL và EAFP trên cùng một dict.
Lỗi thường gặp
| Triệu chứng | Nguyên nhân | Cách sửa |
|---|---|---|
| Chương trình dừng, in traceback | Exception không được bắt | Đọc dòng cuối, thêm try/except |
except không chạy | Bắt sai loại lỗi | In type(e).__name__ để xem thật |
| Lỗi bị che mất | except Exception quá rộng | Bắt loại cụ thể |
| Dữ liệu sai biến mất | except: pass | Ghi log/đếm lỗi |
UnboundLocalError trong except | Dùng biến chưa gán khi try lỗi sớm | Khởi tạo trước try |
| Mất traceback gốc | raise ValueError(...) không kèm from e | Dùng raise ... from e |
int() lỗi dù thấy số | Khoảng trắng ẩn | strip() trước, kiểm tra repr |
finally chạy dù đã return | Hành vi chuẩn | Dùng finally để dọn tài nguyên |
Tóm tắt
- Đọc traceback từ dòng cuối lên.
- Sáu lỗi chính:
KeyError,IndexError,ValueError,TypeError,AttributeError,FileNotFoundError. try/except/else/finally:elsecho luồng thành công,finallyđể dọn dẹp.- Bắt cụ thể, không nuốt lỗi, không bọc cả khối lớn.
raiseđể báo dữ liệu không hợp lệ; exception riêng cho ứng dụng lớn.- EAFP khi tình huống có thể đổi giữa lúc kiểm tra và lúc dùng.
Use ← → to move between lessons