Lỗi và exception có thể dẫn đến hành vi ngoài ý muốn hoặc thậm chí dừng hẳn chương trình. Python cung cấp nhiều hàm và cơ chế để xử lý các vấn đề này, giúp mã bền vững hơn. Trong hướng dẫn này, chúng ta sẽ tìm hiểu các loại lỗi khác nhau và hàm dựng sẵn kèm ví dụ.
Một error là vấn đề nghiêm trọng trong mã mà thường không thể khôi phục khi runtime, như cú pháp sai hoặc thụt lề sai.
Một exception là sự kiện xảy ra khi runtime làm gián đoạn luồng thực thi bình thường nhưng có thể được bắt và xử lý, cho phép chương trình tiếp tục chạy.
Mặc dù exception xảy ra khi runtime, một số lỗi như SyntaxError và IndentationError được bộ phân tích cú pháp phát hiện trước khi chương trình chạy.
Nói đơn giản, error là vấn đề nghiêm trọng mà một ứng dụng bình thường không nên bắt, còn exception là điều kiện mà chương trình nên bắt.
Hãy tìm hiểu thêm về lỗi và exception thông qua các ví dụ. Để chạy nhanh chóng, bạn có thể tạo sổ tay DataLab miễn phí với Python cài sẵn và chứa toàn bộ mẫu mã.
Errors trong Python
Dưới đây là ví dụ về lỗi cú pháp khi trả về bên ngoài hàm là vô nghĩa. Chúng ta không nên xử lý lỗi trong chương trình. Thay vào đó, cần tạo hàm để trả về chuỗi.
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
Input In [4],in<cell line:1>()---->1 test =1/0
ZeroDivisionError: division by zeroĐược hỗ trợ bởi
NameError khá phổ biến khi một biến không được tìm thấy. Chúng ta cũng có thể xử lý exception này bằng cách thay biến hoặc in cảnh báo.
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Input In [5],in<cell line:1>()---->1 y = test
NameError
Được hỗ trợ bởi
Các Exception dựng sẵn trong Python
Dưới đây là danh sách các exception mặc định của Python kèm mô tả:
AssertionError: phát sinh khi câu lệnh assert thất bại.
EOFError: phát sinh khi hàm input() gặp điều kiện cuối tệp.
AttributeError: phát sinh khi gán hoặc tham chiếu thuộc tính thất bại.
TabError: phát sinh khi thụt lề dùng tab và khoảng trắng không nhất quán.
ImportError: phát sinh khi import mô-đun thất bại.
IndexError: xảy ra khi chỉ mục của một dãy vượt phạm vi
KeyboardInterrupt: phát sinh khi người dùng nhấn các phím ngắt (Ctrl + C hoặc Delete).
RuntimeError: xảy ra khi lỗi không thuộc nhóm cụ thể nào.
NameError: phát sinh khi một biến không tồn tại trong phạm vi cục bộ hoặc toàn cục.
MemoryError: phát sinh khi chương trình hết bộ nhớ.
ValueError: xảy ra khi một phép toán hoặc hàm nhận đúng kiểu đối số nhưng giá trị sai.
ZeroDivisionError: phát sinh khi bạn chia một giá trị hoặc biến cho 0.
SyntaxError: do bộ phân tích cú pháp phát sinh khi cú pháp Python sai.
IndentationError: xảy ra khi thụt lề sai.
SystemError: phát sinh khi trình thông dịch phát hiện lỗi nội bộ.
Bạn có thể tìm danh sách đầy đủ lỗi và exception trong Python trong tài liệu.
Tìm hiểu về exception trong Python qua khóa học Lập trình Hướng đối tượng trong Python. Khóa học sẽ hướng dẫn bạn tạo lớp và tận dụng tính kế thừa, đa hình để tái sử dụng và tối ưu mã.
Xử lý Exception với try, except, else và finally
Sau khi tìm hiểu về lỗi và exception, chúng ta sẽ học cách xử lý chúng bằng các khối try, except, else, và finally.
Vậy “xử lý” nghĩa là gì? Thông thường, các lỗi này sẽ dừng thực thi và hiển thị thông báo lỗi. Để tạo hệ thống ổn định, chúng ta cần dự liệu các lỗi và đưa ra giải pháp thay thế hoặc thông báo cảnh báo.
Phần này sẽ nêu chức năng của từng khối và cách dùng chúng để viết mã vững chắc.
Câu lệnh try và except
Cách đơn giản nhất để xử lý exception trong Python là dùng khối try và except.
Chạy mã trong khối try.
Khi một exception phát sinh, thực thi mã trong khối except.
Thay vì dừng lại ở lỗi hoặc exception, mã của chúng ta sẽ chuyển sang giải pháp thay thế.
Ví dụ đơn giản
Trong ví dụ đầu tiên, chúng ta thử in biến x chưa được định nghĩa. Thông thường, việc này sẽ ném lỗi và dừng thực thi, nhưng với khối try và except, ta có thể thay đổi luồng.
Chương trình sẽ chạy mã trong khối try.
Vì x chưa được định nghĩa, nó sẽ chạy khối except và in cảnh báo.
try:print(x)except:print("An exception has occurred!")Được hỗ trợ bởi
Trong ví dụ thứ hai, chúng ta dùng nhiều câu lệnh except để xử lý nhiều loại exception.
Nếu phát sinh ZeroDivisionError, chương trình sẽ in "You cannot divide a value with zero."
Các exception còn lại sẽ in “Something else went wrong.”
Cách này cho phép viết mã linh hoạt, xử lý nhiều exception cùng lúc mà không bị vỡ.
Lưu ý: Tránh dùng except: trống không chỉ rõ loại exception — nó sẽ bắt mọi thứ, bao gồm cả KeyboardInterrupt và SystemExit, khiến chương trình khó thoát và che giấu lỗi không liên quan. Hãy luôn bắt loại exception cụ thể mà bạn mong đợi, như except ZeroDivisionError:.
try:print(1/0)except ZeroDivisionError:print("You cannot divide a value with zero")except:print("Something else went wrong")Được hỗ trợ bởi
Trong đoạn mã dưới đây, chúng ta đọc tệp CSV và khi phát sinh FileNotFoundError, mã sẽ in lỗi và thông điệp bổ sung về tệp data.csv.
Đúng vậy, ta có thể in thông điệp lỗi mặc định mà không làm vỡ quá trình thực thi.
try:withopen('data.csv')asfile:
read_data =file.read()except FileNotFoundError as fnf_error:print(fnf_error)print("Explanation: We cannot load the 'data.csv' file")Được hỗ trợ bởi
[Errno 2] No such fileor directory:'data.csv'
Explanation: We cannot load the 'data.csv'fileĐược hỗ trợ bởi
try với mệnh đề else
Chúng ta đã học về try và except, giờ sẽ tìm hiểu mệnh đề else.
Khi khối try không phát sinh exception, mã sẽ vào khối else. Đây là biện pháp dự phòng khi bạn kỳ vọng một phần script có thể sinh exception. Thường dùng trong phần thiết lập hoặc kiểm tra ngắn, nơi bạn không muốn một số lỗi bị che khuất.
Lưu ý: Trong khối try-except, bạn có thể dùng else sau tất cả các câu lệnh except.
Ví dụ đơn giản
Chúng ta thêm mệnh đề else vào ví dụ ZeroDivisionError. Khi không có exception, hàm print trong khối else được thực thi để hiển thị kết quả.
try:
result =1/3except ZeroDivisionError as err:print(err)else:print(f"Your answer is {result}")Được hỗ trợ bởi
Từ khóa finally trong khối try-except luôn được thực thi, bất kể có exception hay không. Khối finally luôn chạy sau try, except, và else, bất kể exception có được phát sinh hay bắt. Nó rất hữu ích để dọn dẹp tài nguyên và đóng đối tượng, đặc biệt là đóng tệp.
Hàm divide được tạo để xử lý ZeroDivisionError và hiển thị kết quả khi không có exception. Dù kết quả thế nào, nó luôn chạy finally để in “Code by DataCamp” bằng màu xanh lá.
defdivide(x,y):try:
result = x/y
except ZeroDivisionError:print("Please change 'y' argument to non-zero value")except:print("Something went wrong")else:print(f"Your answer is {result}")finally:print("\033[92m Code by DataCamp\033[00m")Được hỗ trợ bởi
Trong kiểm thử đầu tiên, chúng ta chia 1 cho 0, sẽ phát sinh ZeroDivisionError và in thông báo. Như bạn thấy, có một dòng bổ sung sau thông báo lỗi.
Cả ba tình huống đều có điểm chung: mã luôn chạy hàm print trong khối finally.
Nếu bạn mới học Python và muốn lập trình như một lập trình viên thực thụ, hãy thử lộ trình kỹ năng Python Programming. Bạn sẽ học viết mã hiệu quả, hàm Python, kỹ thuật phần mềm, kiểm thử đơn vị và lập trình hướng đối tượng.
Xử lý Exception lồng nhau trong Python
Chúng ta cần xử lý exception lồng nhau khi chuẩn bị cho chương trình xử lý nhiều exception theo trình tự. Ví dụ, có thể thêm một khối try-except khác bên trong mệnh đề else. Vậy nếu câu lệnh đầu không phát sinh exception, hãy kiểm tra câu lệnh thứ hai với phần mã còn lại.
Chỉnh sửa hàm divide
Chúng ta đã chỉnh sửa hàm divide từ ví dụ trước và thêm một khối try-except lồng bên trong mệnh đề else. Vậy nếu không có AttributeError, nó sẽ chạy else và kiểm tra đoạn mã mới với exception ZeroDivisionError.
defdivide(x,y):try:
value =50
x.append(value)except AttributeError as atr_err:print(atr_err)else:try:
result =[i / y for i in x]print( result )except ZeroDivisionError:print("Please change 'y' argument to non-zero value")finally:print("\033[92m Code by DataCamp\033[00m")Được hỗ trợ bởi
Trong kịch bản đầu tiên, chúng ta truyền danh sách bốn giá trị x và mẫu số 3. Script sẽ thêm 50 vào danh sách, chia từng giá trị cho 3 và hiển thị kết quả.
Please change 'y' argument to non-zero value
Code by DataCampĐược hỗ trợ bởi
Ví dụ chỉnh sửa tệp
Hãy xem ví dụ thực tế hơn về tải tệp, ghi văn bản và đóng tệp.
Hàm file_editor() sẽ:
Kiểm tra exception FileNotFoundError cho hàm open().
Nếu không có exception bên ngoài, sẽ kiểm tra exception của hàm write().
Bất kể thế nào, sau khi mở tệp, nó sẽ đóng tệp bằng khối finally.
Nếu khối try bên ngoài phát sinh exception, nó sẽ trả về thông báo lỗi với đường dẫn tệp không hợp lệ.
deffile_editor(path,text):try:
data =open(path)try:
data.write(text)except:print("Unable to write the data. Please add an append: 'a' or write: 'w' parameter to the open() function.")finally:
data.close()except:print(f"{path} file is not found!!")Được hỗ trợ bởi
Trong kịch bản đầu, chúng ta truyền đường dẫn tệp và văn bản.
path ="data.txt"
text ="DataLab: Share your data analysis in a cloud-based environment--no installation required."
file_editor(path,text)Được hỗ trợ bởi
Xử lý exception lồng nhau không được khuyến nghị vì làm phức tạp việc xử lý; các lập trình viên thường dùng nhiều khối try-except để tạo luồng xử lý tuần tự đơn giản.
Lưu ý: bạn cũng có thể thêm khối try-except lồng bên trong try hoặc except. Tùy thuộc yêu cầu của bạn.
Tự phát sinh Exception trong Python
Không chỉ bắt exception, bạn còn có thể cố ý phát sinh chúng bằng từ khóa raise. Điều này hữu ích để áp đặt quy tắc nghiệp vụ hoặc kiểm tra đầu vào. Nó tạo tín hiệu cho thấy có vấn đề trước khi gây ra lỗi khó gỡ hơn về sau.
Để ném một exception, chúng ta dùng từ khóa raise theo sau là tên exception.
Ví dụ raise ValueError
Ta có thể đơn giản phát sinh exception bằng cách thêm từ khóa raise trong câu lệnh if/else.
Trong ví dụ, chúng ta phát sinh ValueError nếu giá trị vượt 1.000. Chúng ta đổi giá trị thành 2.000, làm câu lệnh ifTRUE và phát sinh ValueError với thông điệp tùy chỉnh. Thông điệp tùy chỉnh giúp bạn nhanh chóng xác định vấn đề.
value =2_000if value >1_000:# raise the ValueErrorraise ValueError("Please add a value lower than 1,000")else:print("Congratulations! You are the winner!!")Được hỗ trợ bởi
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)---->4raise ValueError("Please add a value lower than 1,000")5else:6print("Congratulations! You are the winner!!")
ValueError: Please add a value lower than 1,000Được hỗ trợ bởi
Ví dụ raise Exception
Chúng ta cũng có thể phát sinh bất kỳ exception dựng sẵn nào nếu điều kiện được thỏa. Ở đây, chúng ta phát sinh Exception chung với thông điệp lỗi.
if value >1_000:# raise the Exceptionraise Exception("Please add a value lower than 1,000")else:print("Congratulations! You are the winner!!")Được hỗ trợ bởi
---------------------------------------------------------------------------
Exception Traceback (most recent call last)---->3raise Exception("Please add a value lower than 1,000")4else:5print("Congratulations! You are the winner!!")
Exception: Please add a value lower than 1,000Được hỗ trợ bởi
Ví dụ xử lý exception được raise
Chúng ta cũng có thể tạo exception tùy chỉnh và xử lý bằng khối try-except.
Trong ví dụ, chúng ta đặt ví dụ ValueError vào trong khối try.
Vậy nó hoạt động thế nào? Thay vì ném exception và kết thúc chương trình, nó sẽ hiển thị thông điệp lỗi do chúng ta cung cấp.
value =2_000try:if value >1_000:# raise the ValueErrorraise ValueError("Please add a value lower than 1,000")else:print("Congratulations! You are the winner!!")# if false then raise the value errorexcept ValueError as e:print(e)Được hỗ trợ bởi
Cách xử lý exception này giúp chúng ta chuẩn bị cho các lỗi không được Python bao quát và đặc thù yêu cầu ứng dụng của bạn.
Ngoài việc raise độc lập, bạn có thể xâu chuỗi exception bằng raise ... from. Cách này giữ lại exception gốc làm ngữ cảnh, giúp traceback rõ ràng hơn khi một lỗi gây ra lỗi khác.
try:open("config.json")except FileNotFoundError as e:raise RuntimeError("App failed to start: config file missing")from eĐược hỗ trợ bởi
FileNotFoundError:[Errno 2] No such fileor directory:'config.json'
The above exception was the direct cause of the following exception:
RuntimeError: App failed to start: config file missingĐược hỗ trợ bởi
Phần from e đặt thuộc tính __cause__ trên exception mới, giúp bất kỳ ai đọc traceback đều thấy chính xác điều gì kích hoạt nó. Điều này đặc biệt hữu ích trong ứng dụng lớn nơi exception đi qua nhiều lớp mã.
Có gì mới từ Python 3.10 - 3.13 cho xử lý Exception?
Một số phát triển quan trọng đã được thêm vào các phiên bản Python mới như 3.10 và 3.11 kể từ khi bài viết gốc được viết:
1. So khớp mẫu cấu trúc trong Python 3.10
Python 3.10 giới thiệu so khớp mẫu cấu trúc (structural pattern matching), có thể dùng để xử lý exception thanh thoát hơn bằng cách so khớp loại lỗi trong câu lệnh match. Điều này đặc biệt hữu ích khi xử lý nhiều loại exception theo cách dễ đọc hơn. Ví dụ:
try:
result =1/0except Exception as e:match e:case ZeroDivisionError():print("You cannot divide by zero.")case NameError():print("Variable not defined.")case_:print("An unexpected error occurred.")Được hỗ trợ bởi
2. Vị trí lỗi chi tiết trong traceback (Python 3.11)
Traceback trong Python 3.11 giờ hiển thị vị trí lỗi chính xác, giúp gỡ lỗi dễ hơn.
Với Python 3.11, traceback chỉ đúng biểu thức con gây exception:
ZeroDivisionError: division by zero
(1+2)*(3/0)^Được hỗ trợ bởi
3. Nhóm exception và except* (Python 3.11)
Python 3.11 giới thiệu nhóm exception để xử lý nhiều exception trong một khối bằng except*. Hữu ích cho code bất đồng bộ hoặc tình huống có thể sinh nhiều exception. Ví dụ:
try:raise ExceptionGroup("Multiple errors",[ValueError("Bad value"), TypeError("Bad type")])except* ValueError as ve:print(f"Handling ValueError: {ve}")except* TypeError as te:print(f"Handling TypeError: {te}")Được hỗ trợ bởi
4. Phương thức add_note cho exception (Python 3.11)
Exception giờ có phương thức add_note(), cho phép thêm ghi chú tùy chỉnh để gỡ lỗi tốt hơn. Ví dụ:
try:raise ValueError("Invalid input")except ValueError as e:
e.add_note("This happened while processing user input.")
e.add_note("Consider validating input before processing.")raiseĐược hỗ trợ bởi
ValueError: Invalid input
Notes:- This happened while processing user input.- Consider validating input before processing.Được hỗ trợ bởi
5. PEP 654: Xử lý tốt hơn cho exception lồng nhau
Với nhóm exception, các exception lồng nhau nay dễ gỡ và xử lý hơn, đặc biệt trong quy trình phức tạp như đa xử lý hoặc tác vụ bất đồng bộ.
6. Thông điệp lỗi được cải thiện (Python 3.12)
Python 3.12 tiếp tục nỗ lực lâu dài nhằm khiến thông điệp lỗi cụ thể và hữu ích hơn. Ví dụ, thông điệp NameError giờ gợi ý các biến có tên tương tự trong phạm vi, và lỗi thuộc tính trên các kiểu phổ biến đưa ra gợi ý chính xác hơn:
# Python 3.12+
my_list =[1,2,3]
my_list.appendd(4)Được hỗ trợ bởi
AttributeError:'list'object has no attribute 'appendd'. Did you mean:'append'?Được hỗ trợ bởi
7. Traceback có màu (Python 3.13)
Python 3.13 giới thiệu traceback tô màu mặc định trong trình thông dịch tương tác. Loại lỗi, số dòng và thông điệp được làm nổi bật bằng màu sắc khác nhau, giúp quét và hiểu vấn đề nhanh hơn — đặc biệt với traceback dài có nhiều exception xâu chuỗi.
Kết luận
Cả kiểm thử đơn vị và xử lý exception đều là phần cốt lõi của lập trình Python, giúp mã của bạn sẵn sàng đưa vào sản xuất và hạn chế lỗi. Trong hướng dẫn này, chúng ta đã học về exception và error trong Python và cách xử lý. Bên cạnh đó, chúng ta đã tìm hiểu các khối try-except lồng nhau phức tạp và tạo khối exception tùy chỉnh theo yêu cầu.
Những công cụ và cơ chế này rất thiết yếu, nhưng phần lớn công việc được thực hiện qua các khối try và except đơn giản. Trong đó try dò tìm exception do mã gây ra và except xử lý các exception đó.
Nếu bạn thấy rối và không biết bắt đầu từ đâu, hãy hoàn thành khóa Giới thiệu về Hàm trong Python để hiểu về phạm vi, hàm lambda và xử lý lỗi. Bạn cũng có thể ghi danh lộ trình nghề nghiệp Python Programmer để tích lũy kỹ năng nghề nghiệp và trở thành lập trình viên Python chuyên nghiệp.
Là một nhà khoa học dữ liệu được chứng nhận, tôi đam mê tận dụng công nghệ tiên tiến để tạo ra các ứng dụng học máy đổi mới. Với nền tảng vững chắc về nhận dạng giọng nói, phân tích và báo cáo dữ liệu, MLOps, AI hội thoại và NLP, tôi đã rèn giũa kỹ năng phát triển các hệ thống thông minh có thể tạo ra tác động thực sự. Bên cạnh chuyên môn kỹ thuật, tôi cũng là một người truyền đạt tốt, có khả năng chắt lọc các khái niệm phức tạp thành ngôn ngữ rõ ràng, súc tích. Nhờ đó, tôi trở thành một blogger được nhiều người quan tâm trong lĩnh vực khoa học dữ liệu, chia sẻ góc nhìn và kinh nghiệm với cộng đồng các chuyên gia dữ liệu ngày càng lớn. Hiện tại, tôi tập trung vào sáng tạo và biên tập nội dung, làm việc với các mô hình ngôn ngữ lớn để phát triển nội dung mạnh mẽ và hấp dẫn, giúp doanh nghiệp và cá nhân tận dụng tối đa dữ liệu của mình.
FAQs
Sự khác nhau giữa error và exception trong Python là gì?⌄
Một error trong Python thường là vấn đề nghiêm trọng khiến chương trình không thể tiếp tục, như lỗi cú pháp, cho thấy cấu trúc mã không đúng. Ngược lại, một exception là điều kiện làm gián đoạn luồng bình thường của chương trình nhưng có thể được xử lý trong chương trình bằng các khối try-except, cho phép chương trình tiếp tục thực thi.
Bạn có thể bắt nhiều exception trong một khối try-except không?⌄
Có. Python cho phép bạn bắt nhiều exception trong một khối try-except bằng cách dùng một tuple các loại exception. Ví dụ: except (TypeError, ValueError):. Khối này sẽ xử lý cả TypeError hoặc ValueError trong cùng một khối.
Làm thế nào để tạo exception tùy chỉnh trong Python?⌄
Bạn có thể tạo exception tùy chỉnh bằng cách định nghĩa một lớp mới kế thừa từ lớp dựng sẵn Exception. Ví dụ:
Mục đích của khối else trong cấu trúc try-except là gì?⌄
Khối else được thực thi nếu khối try không phát sinh exception. Nó hữu ích cho đoạn mã chỉ nên chạy khi khối try thành công, giúp mã gọn gàng và tách biệt xử lý lỗi khỏi luồng thực thi bình thường.
Vì sao khối finally quan trọng trong xử lý exception?⌄
Khối finally luôn được thực thi, bất kể có exception hay không. Thường dùng cho thao tác dọn dẹp, như đóng tệp hoặc giải phóng tài nguyên, để đảm bảo luôn được thực hiện trong mọi trường hợp.
Làm thế nào để bỏ qua exception trong Python?⌄
Bạn có thể bỏ qua exception bằng cách dùng bộ quản lý ngữ cảnh contextlib.suppress. Nó cho phép chỉ định các loại exception sẽ bị bỏ qua. Ví dụ:
from contextlib import suppress
with suppress(FileNotFoundError):open('non_existent_file.txt')Được hỗ trợ bởi
Điều gì xảy ra nếu một exception không được bắt trong chương trình Python?⌄
Nếu một exception không được bắt, nó sẽ lan truyền lên ngăn xếp lời gọi; nếu vẫn không được xử lý, chương trình sẽ kết thúc và in traceback ra console cho biết nơi exception xảy ra.
Bạn có thể ném lại exception sau khi đã bắt nó không?⌄
Có, bạn có thể ném lại exception bằng câu lệnh raise không đối số trong khối except. Điều này hữu ích khi bạn muốn ghi log lỗi hoặc thực hiện hành động khác trước khi để exception lan truyền tiếp.
Có cách nào xử lý tất cả exception trong Python không?⌄
Mặc dù có thể bắt mọi exception bằng mệnh đề except: trống, nhưng nhìn chung không khuyến nghị vì có thể bắt cả những exception không ngờ tới và khiến việc gỡ lỗi khó khăn. Tốt hơn nên bắt các exception cụ thể hoặc dùng except Exception: để tránh bắt các exception kết thúc hệ thống như SystemExit và KeyboardInterrupt.
Phân biệt thời điểm xảy ra giữa lỗi cú pháp và exception khi runtime như thế nào?⌄
Lỗi cú pháp được phát hiện tại thời điểm biên dịch, nghĩa là trình thông dịch Python bắt các lỗi này trước khi chương trình bắt đầu chạy. Exception khi runtime xảy ra trong quá trình thực thi khi trình thông dịch gặp thao tác không thể thực hiện, như chia cho 0 hoặc truy cập biến không tồn tại.
Lỗi và exception có thể dẫn đến hành vi ngoài ý muốn hoặc thậm chí dừng hẳn chương trình. Python cung cấp nhiều hàm và cơ chế để xử lý các vấn đề này, giúp mã bền vững hơn. Trong hướng dẫn này, chúng ta sẽ tìm hiểu các loại lỗi khác nhau và hàm dựng sẵn kèm ví dụ.
Mặc dù exception xảy ra khi runtime, một số lỗi như
SyntaxErrorvàIndentationErrorđược bộ phân tích cú pháp phát hiện trước khi chương trình chạy.Nói đơn giản, error là vấn đề nghiêm trọng mà một ứng dụng bình thường không nên bắt, còn exception là điều kiện mà chương trình nên bắt.
Hãy tìm hiểu thêm về lỗi và exception thông qua các ví dụ. Để chạy nhanh chóng, bạn có thể tạo sổ tay DataLab miễn phí với Python cài sẵn và chứa toàn bộ mẫu mã.
Errors trong Python
Dưới đây là ví dụ về lỗi cú pháp khi trả về bên ngoài hàm là vô nghĩa. Chúng ta không nên xử lý lỗi trong chương trình. Thay vào đó, cần tạo hàm để trả về chuỗi.
Chúng ta đã tạo hàm nhưng thụt lề sai. Không nên xử lý lỗi thụt lề khi runtime. Có thể sửa thủ công hoặc dùng công cụ định dạng mã.
Exceptions trong Python
Chúng ta gặp
ZeroDivisionError(Exception). Có thể xử lý khi runtime bằng cách dùng các khốitryvàexcept.NameErrorkhá phổ biến khi một biến không được tìm thấy. Chúng ta cũng có thể xử lý exception này bằng cách thay biến hoặc in cảnh báo.Các Exception dựng sẵn trong Python
Dưới đây là danh sách các exception mặc định của Python kèm mô tả:
AssertionError: phát sinh khi câu lệnh assert thất bại.EOFError: phát sinh khi hàminput()gặp điều kiện cuối tệp.AttributeError: phát sinh khi gán hoặc tham chiếu thuộc tính thất bại.TabError: phát sinh khi thụt lề dùng tab và khoảng trắng không nhất quán.ImportError: phát sinh khi import mô-đun thất bại.IndexError: xảy ra khi chỉ mục của một dãy vượt phạm viKeyboardInterrupt: phát sinh khi người dùng nhấn các phím ngắt (Ctrl + C hoặc Delete).RuntimeError: xảy ra khi lỗi không thuộc nhóm cụ thể nào.NameError: phát sinh khi một biến không tồn tại trong phạm vi cục bộ hoặc toàn cục.MemoryError: phát sinh khi chương trình hết bộ nhớ.ValueError: xảy ra khi một phép toán hoặc hàm nhận đúng kiểu đối số nhưng giá trị sai.ZeroDivisionError: phát sinh khi bạn chia một giá trị hoặc biến cho 0.SyntaxError: do bộ phân tích cú pháp phát sinh khi cú pháp Python sai.IndentationError: xảy ra khi thụt lề sai.SystemError: phát sinh khi trình thông dịch phát hiện lỗi nội bộ.Bạn có thể tìm danh sách đầy đủ lỗi và exception trong Python trong tài liệu.
Tìm hiểu về exception trong Python qua khóa học Lập trình Hướng đối tượng trong Python. Khóa học sẽ hướng dẫn bạn tạo lớp và tận dụng tính kế thừa, đa hình để tái sử dụng và tối ưu mã.
Xử lý Exception với try, except, else và finally
Sau khi tìm hiểu về lỗi và exception, chúng ta sẽ học cách xử lý chúng bằng các khối
try,except,else, vàfinally.Vậy “xử lý” nghĩa là gì? Thông thường, các lỗi này sẽ dừng thực thi và hiển thị thông báo lỗi. Để tạo hệ thống ổn định, chúng ta cần dự liệu các lỗi và đưa ra giải pháp thay thế hoặc thông báo cảnh báo.
Phần này sẽ nêu chức năng của từng khối và cách dùng chúng để viết mã vững chắc.
Câu lệnh try và except
Cách đơn giản nhất để xử lý exception trong Python là dùng khối
tryvàexcept.Chạy mã trong khối
try.Khi một exception phát sinh, thực thi mã trong khối
except.Thay vì dừng lại ở lỗi hoặc exception, mã của chúng ta sẽ chuyển sang giải pháp thay thế.
Ví dụ đơn giản
Trong ví dụ đầu tiên, chúng ta thử in biến
xchưa được định nghĩa. Thông thường, việc này sẽ ném lỗi và dừng thực thi, nhưng với khốitryvàexcept, ta có thể thay đổi luồng.try.xchưa được định nghĩa, nó sẽ chạy khối except và in cảnh báo.Ví dụ với nhiều except
Trong ví dụ thứ hai, chúng ta dùng nhiều câu lệnh
exceptđể xử lý nhiều loại exception.ZeroDivisionError, chương trình sẽ in "You cannot divide a value with zero."Cách này cho phép viết mã linh hoạt, xử lý nhiều exception cùng lúc mà không bị vỡ.
Lưu ý: Tránh dùng
except:trống không chỉ rõ loại exception — nó sẽ bắt mọi thứ, bao gồm cảKeyboardInterruptvàSystemExit, khiến chương trình khó thoát và che giấu lỗi không liên quan. Hãy luôn bắt loại exception cụ thể mà bạn mong đợi, nhưexcept ZeroDivisionError:.Ví dụ đọc tệp
Bây giờ, hãy xem một ví dụ thực tế hơn.
Trong đoạn mã dưới đây, chúng ta đọc tệp CSV và khi phát sinh
FileNotFoundError, mã sẽ in lỗi và thông điệp bổ sung về tệpdata.csv.Đúng vậy, ta có thể in thông điệp lỗi mặc định mà không làm vỡ quá trình thực thi.
try với mệnh đề else
Chúng ta đã học về
tryvàexcept, giờ sẽ tìm hiểu mệnh đềelse.Khi khối
trykhông phát sinh exception, mã sẽ vào khốielse. Đây là biện pháp dự phòng khi bạn kỳ vọng một phần script có thể sinh exception. Thường dùng trong phần thiết lập hoặc kiểm tra ngắn, nơi bạn không muốn một số lỗi bị che khuất.Lưu ý: Trong khối try-except, bạn có thể dùng
elsesau tất cả các câu lệnhexcept.Ví dụ đơn giản
Chúng ta thêm mệnh đề
elsevào ví dụZeroDivisionError. Khi không có exception, hàm print trong khốielseđược thực thi để hiển thị kết quả.Ví dụ IndexError với else
Hãy tìm hiểu thêm bằng cách tạo một hàm đơn giản và kiểm thử nhiều tình huống.
Hàm
find_nth_value()nhậnx(list) vàn(chỉ số) làm đối số. Chúng ta tạo các khốitry,except, vàelseđể xử lýIndexError.Danh sách
xcó bốn giá trị, và chúng ta sẽ kiểm thử với chỉ số thứ 6 và 2.IndexErrorđược phát sinh, và chúng ta thấy thông điệp lỗi mặc định “list index out of range.”else.Từ khóa finally trong Python
Từ khóa
finallytrong khốitry-exceptluôn được thực thi, bất kể có exception hay không. Khốifinallyluôn chạy sautry,except, vàelse, bất kể exception có được phát sinh hay bắt. Nó rất hữu ích để dọn dẹp tài nguyên và đóng đối tượng, đặc biệt là đóng tệp.Hàm
divideđược tạo để xử lýZeroDivisionErrorvà hiển thị kết quả khi không có exception. Dù kết quả thế nào, nó luôn chạyfinallyđể in “Code by DataCamp” bằng màu xanh lá.Trong kiểm thử đầu tiên, chúng ta chia 1 cho 0, sẽ phát sinh
ZeroDivisionErrorvà in thông báo. Như bạn thấy, có một dòng bổ sung sau thông báo lỗi.Khi đưa vào đầu vào hợp lệ, hàm hiển thị kết quả bằng cách thực thi các khối
elsevàfinally.Thay vì số nguyên, chúng ta đưa một chuỗi làm đối số thứ hai, điều này phát sinh một exception khác với
ZeroDivisionErrorvà có thông điệp khác.Cả ba tình huống đều có điểm chung: mã luôn chạy hàm print trong khối
finally.Nếu bạn mới học Python và muốn lập trình như một lập trình viên thực thụ, hãy thử lộ trình kỹ năng Python Programming. Bạn sẽ học viết mã hiệu quả, hàm Python, kỹ thuật phần mềm, kiểm thử đơn vị và lập trình hướng đối tượng.
Xử lý Exception lồng nhau trong Python
Chúng ta cần xử lý exception lồng nhau khi chuẩn bị cho chương trình xử lý nhiều exception theo trình tự. Ví dụ, có thể thêm một khối try-except khác bên trong mệnh đề
else. Vậy nếu câu lệnh đầu không phát sinh exception, hãy kiểm tra câu lệnh thứ hai với phần mã còn lại.Chỉnh sửa hàm divide
Chúng ta đã chỉnh sửa hàm
dividetừ ví dụ trước và thêm một khối try-except lồng bên trong mệnh đềelse. Vậy nếu không cóAttributeError, nó sẽ chạyelsevà kiểm tra đoạn mã mới với exceptionZeroDivisionError.Trong kịch bản đầu tiên, chúng ta truyền danh sách bốn giá trị
xvà mẫu số 3. Script sẽ thêm 50 vào danh sách, chia từng giá trị cho 3 và hiển thị kết quả.Hàm thực thi thành công mà không phát sinh exception.
Thay vì danh sách, chúng ta truyền một số nguyên vào đối số thứ nhất, dẫn đến
AttributeError.Trong kịch bản cuối, chúng ta truyền danh sách, nhưng 0 là đối số thứ hai nên phát sinh
ZeroDivisionErrortrong mệnh đềelse.Ví dụ chỉnh sửa tệp
Hãy xem ví dụ thực tế hơn về tải tệp, ghi văn bản và đóng tệp.
Hàm
file_editor()sẽ:FileNotFoundErrorcho hàmopen().write().finally.Trong kịch bản đầu, chúng ta truyền đường dẫn tệp và văn bản.
Exception bên ngoài được phát sinh.
Để khắc phục lỗi không tìm thấy tệp, chúng ta cần tạo tệp
data.txtbằng lệnh Linuxecho.Sau đó, chạy lại hàm
file_editor().Exception bên trong được phát sinh, vì hàm
write()không thể thêm văn bản.Để giải quyết, cần đổi dòng thứ ba từ
data = open(path)sangdata = open(path, 'a'). Điều này cho phép nối thêm văn bản vào tệp.Sau khi chạy lại hàm, chúng ta đã thêm văn bản vào tệp thành công.
Xử lý exception lồng nhau không được khuyến nghị vì làm phức tạp việc xử lý; các lập trình viên thường dùng nhiều khối
try-exceptđể tạo luồng xử lý tuần tự đơn giản.Lưu ý: bạn cũng có thể thêm khối
try-exceptlồng bên trongtryhoặcexcept. Tùy thuộc yêu cầu của bạn.Tự phát sinh Exception trong Python
Không chỉ bắt exception, bạn còn có thể cố ý phát sinh chúng bằng từ khóa
raise. Điều này hữu ích để áp đặt quy tắc nghiệp vụ hoặc kiểm tra đầu vào. Nó tạo tín hiệu cho thấy có vấn đề trước khi gây ra lỗi khó gỡ hơn về sau.Để ném một exception, chúng ta dùng từ khóa
raisetheo sau là tên exception.Ví dụ raise ValueError
Ta có thể đơn giản phát sinh exception bằng cách thêm từ khóa raise trong câu lệnh
if/else.Trong ví dụ, chúng ta phát sinh
ValueErrornếu giá trị vượt 1.000. Chúng ta đổi giá trị thành 2.000, làm câu lệnhifTRUEvà phát sinhValueErrorvới thông điệp tùy chỉnh. Thông điệp tùy chỉnh giúp bạn nhanh chóng xác định vấn đề.Ví dụ raise Exception
Chúng ta cũng có thể phát sinh bất kỳ exception dựng sẵn nào nếu điều kiện được thỏa. Ở đây, chúng ta phát sinh Exception chung với thông điệp lỗi.
Ví dụ xử lý exception được raise
Chúng ta cũng có thể tạo exception tùy chỉnh và xử lý bằng khối
try-except.Trong ví dụ, chúng ta đặt ví dụ ValueError vào trong khối
try.Vậy nó hoạt động thế nào? Thay vì ném exception và kết thúc chương trình, nó sẽ hiển thị thông điệp lỗi do chúng ta cung cấp.
Cách xử lý exception này giúp chúng ta chuẩn bị cho các lỗi không được Python bao quát và đặc thù yêu cầu ứng dụng của bạn.
Xâu chuỗi exception với raise ... from
Ngoài việc raise độc lập, bạn có thể xâu chuỗi exception bằng
raise ... from. Cách này giữ lại exception gốc làm ngữ cảnh, giúp traceback rõ ràng hơn khi một lỗi gây ra lỗi khác.Phần
from eđặt thuộc tính__cause__trên exception mới, giúp bất kỳ ai đọc traceback đều thấy chính xác điều gì kích hoạt nó. Điều này đặc biệt hữu ích trong ứng dụng lớn nơi exception đi qua nhiều lớp mã.Có gì mới từ Python 3.10 - 3.13 cho xử lý Exception?
Một số phát triển quan trọng đã được thêm vào các phiên bản Python mới như 3.10 và 3.11 kể từ khi bài viết gốc được viết:
1. So khớp mẫu cấu trúc trong Python 3.10
Python 3.10 giới thiệu so khớp mẫu cấu trúc (structural pattern matching), có thể dùng để xử lý exception thanh thoát hơn bằng cách so khớp loại lỗi trong câu lệnh
match. Điều này đặc biệt hữu ích khi xử lý nhiều loại exception theo cách dễ đọc hơn. Ví dụ:2. Vị trí lỗi chi tiết trong traceback (Python 3.11)
Traceback trong Python 3.11 giờ hiển thị vị trí lỗi chính xác, giúp gỡ lỗi dễ hơn.
Trước Python 3.11:
Với Python 3.11, traceback chỉ đúng biểu thức con gây exception:
3. Nhóm exception và except* (Python 3.11)
Python 3.11 giới thiệu nhóm exception để xử lý nhiều exception trong một khối bằng
except*. Hữu ích cho code bất đồng bộ hoặc tình huống có thể sinh nhiều exception. Ví dụ:4. Phương thức add_note cho exception (Python 3.11)
Exception giờ có phương thức
add_note(), cho phép thêm ghi chú tùy chỉnh để gỡ lỗi tốt hơn. Ví dụ:5. PEP 654: Xử lý tốt hơn cho exception lồng nhau
Với nhóm exception, các exception lồng nhau nay dễ gỡ và xử lý hơn, đặc biệt trong quy trình phức tạp như đa xử lý hoặc tác vụ bất đồng bộ.
6. Thông điệp lỗi được cải thiện (Python 3.12)
Python 3.12 tiếp tục nỗ lực lâu dài nhằm khiến thông điệp lỗi cụ thể và hữu ích hơn. Ví dụ, thông điệp
NameErrorgiờ gợi ý các biến có tên tương tự trong phạm vi, và lỗi thuộc tính trên các kiểu phổ biến đưa ra gợi ý chính xác hơn:7. Traceback có màu (Python 3.13)
Python 3.13 giới thiệu traceback tô màu mặc định trong trình thông dịch tương tác. Loại lỗi, số dòng và thông điệp được làm nổi bật bằng màu sắc khác nhau, giúp quét và hiểu vấn đề nhanh hơn — đặc biệt với traceback dài có nhiều exception xâu chuỗi.
Kết luận
Cả kiểm thử đơn vị và xử lý exception đều là phần cốt lõi của lập trình Python, giúp mã của bạn sẵn sàng đưa vào sản xuất và hạn chế lỗi. Trong hướng dẫn này, chúng ta đã học về exception và error trong Python và cách xử lý. Bên cạnh đó, chúng ta đã tìm hiểu các khối try-except lồng nhau phức tạp và tạo khối exception tùy chỉnh theo yêu cầu.
Những công cụ và cơ chế này rất thiết yếu, nhưng phần lớn công việc được thực hiện qua các khối
tryvàexceptđơn giản. Trong đótrydò tìm exception do mã gây ra vàexceptxử lý các exception đó.Nếu bạn thấy rối và không biết bắt đầu từ đâu, hãy hoàn thành khóa Giới thiệu về Hàm trong Python để hiểu về phạm vi, hàm lambda và xử lý lỗi. Bạn cũng có thể ghi danh lộ trình nghề nghiệp Python Programmer để tích lũy kỹ năng nghề nghiệp và trở thành lập trình viên Python chuyên nghiệp.