Thông báo: Download 4 khóa học Python từ cơ bản đến nâng cao tại đây.
Tìm hiểu về các ngoại lệ trong Python
Trong quá trình lập trình, việc gặp phải các lỗi là điều không thể tránh khỏi. Những lỗi này, được gọi là ngoại lệ, có thể xảy ra do nhiều nguyên nhân khác nhau, từ việc nhập sai dữ liệu đến các vấn đề logic trong mã nguồn. Trong Python, việc xử lý các ngoại lệ không chỉ giúp cho chương trình của bạn chạy mượt mà hơn mà còn nâng cao trải nghiệm người dùng bằng cách cung cấp thông tin rõ ràng về những gì đã xảy ra. Bài viết này sẽ đưa bạn vào thế giới của các ngoại lệ trong Python, giải thích cách thức hoạt động của chúng và cách bạn có thể xử lý chúng một cách hiệu quả, đảm bảo chương trình của bạn không bị dừng lại một cách đột ngột khi gặp phải lỗi.
Giới thiệu về ngoại lệ trong Python
Trong Python, ngoại lệ là các đối tượng thuộc các lớp ngoại lệ. Tất cả các lớp ngoại lệ đều là các lớp con của lớp BaseException
. Tuy nhiên, hầu hết các lớp ngoại lệ tích hợp đều kế thừa từ lớp Exception
, là lớp con của lớp BaseException
.
Ví dụ sau đây định nghĩa một danh sách có ba phần tử và cố gắng truy cập phần tử thứ tư:
colors = ['red', 'green', 'blue'] print(colors[3])
Chỉ số không hợp lệ đã gây ra ngoại lệ IndexError
như mong đợi:
Bài viết này được đăng tại [free tuts .net]
IndexError: list index out of range
Khi một ngoại lệ xảy ra, Python sẽ dừng chương trình trừ khi bạn xử lý nó. Để xử lý ngoại lệ, bạn sử dụng câu lệnh try...except
. Ví dụ:
colors = ['red', 'green', 'blue'] try: print(colors[3]) except IndexError as e: print(e) print('Tiếp tục thực thi')
Kết quả:
<class 'IndexError'> - list index out of range Tiếp tục thực thi
Trong ví dụ này, chúng ta đã sử dụng câu lệnh try...except
để xử lý ngoại lệ IndexError
. Như bạn thấy từ kết quả, chương trình tiếp tục thực thi sau câu lệnh try...except
.
Phân cấp lớp ngoại lệ
Lớp IndexError
kế thừa từ lớp LookupError
, mà kế thừa từ lớp Exception
. Bạn có thể bắt ngoại lệ từ lớp LookupError
hoặc lớp Exception
khi xảy ra ngoại lệ IndexError
. Ví dụ:
colors = ['red', 'green', 'blue'] try: print(colors[3]) except LookupError as e: print(e.__class__, '-', e) print('Tiếp tục thực thi')
Kết quả:
<class 'IndexError'> - list index out of range Tiếp tục thực thi
Trong ví dụ này, ngoại lệ vẫn là IndexError
mặc dù chúng ta bắt ngoại lệ từ lớp LookupError
. Do đó, khi bạn xử lý một ngoại lệ, bộ xử lý ngoại lệ sẽ bắt loại ngoại lệ mà bạn chỉ định và bất kỳ lớp con nào của nó.
Chương trình sẽ hoạt động tương tự nếu bạn sử dụng lớp Exception
thay vì lớp LookupError
.
Ví dụ về xử lý ngoại lệ trong Python
Ví dụ sau đây định nghĩa một hàm chia, trả về kết quả của a chia cho b:
def division(a, b): return a / b
c = division(10, 0)
Kết quả:
ZeroDivisionError: division by zero
Trong ví dụ này, nếu b bằng 0, ngoại lệ ZeroDivisionError
sẽ xảy ra. Để xử lý ngoại lệ này, bạn sử dụng câu lệnh try...except
như sau:
def division(a, b): try: return { 'success': True, 'message': 'OK', 'result': a / b } except ZeroDivisionError as e: return { 'success': False, 'message': 'b không thể bằng 0', 'result': None }
Kết quả của hàm sẽ là một từ điển có ba phần tử:
success
: một giá trị boolean cho biết liệu thao tác có thành công hay không.message
: thông báo lỗi hoặc thông báo thành công.result
: lưu trữ kết quả của a chia cho b hoặcNone
nếu b bằng 0.
Kết quả nếu xảy ra ngoại lệ ZeroDivisionError
:
{'success': False, 'message': 'b không thể bằng 0', 'result': None}
Bây giờ, nếu bạn không bắt ngoại lệ ZeroDivisionError
mà chỉ bắt loại ngoại lệ tổng quát hơn như lớp Exception
:
def division(a, b): try: return { 'success': True, 'message': 'OK', 'result': a / b } except Exception as e: return { 'success': False, 'message': 'b không thể bằng 0', 'result': None }
Kết quả sẽ vẫn như trước vì câu lệnh try...except
cũng bắt được các ngoại lệ là lớp con của lớp Exception
.
Tuy nhiên, nếu bạn truyền vào hai chuỗi thay vì hai số cho hàm division()
:
def division(a, b): try: return { 'success': True, 'message': 'OK', 'result': a / b } except Exception as e: return { 'success': False, 'message': 'b không thể bằng 0', 'result': None }
Kết quả:
{'success': False, 'message': 'b không thể bằng 0', 'result': None}
Trong ví dụ này, ngoại lệ không phải là ZeroDivisionError
mà là TypeError
. Tuy nhiên, mã vẫn xử lý nó như ngoại lệ ZeroDivisionError
.
Vì vậy, bạn nên luôn xử lý các ngoại lệ từ cụ thể đến tổng quát. Ví dụ:
def division(a, b): try: return { 'success': True, 'message': 'OK', 'result': a / b } except TypeError as e: return { 'success': False, 'message': 'Cả a và b phải là số', 'result': None } except ZeroDivisionError as e: return { 'success': False, 'message': 'b không thể bằng 0', 'result': None } except Exception as e: return { 'success': False, 'message': str(e), 'result': None }
Trong ví dụ này, chúng ta đã bắt ngoại lệ TypeError
, ZeroDivisionError
và Exception
theo thứ tự xuất hiện trong câu lệnh try...except
.
Nếu mã xử lý các ngoại lệ khác nhau là giống nhau, bạn có thể nhóm tất cả ngoại lệ thành một như sau:
def division(a, b): try: return { 'success': True, 'message': 'OK', 'result': a / b } except (TypeError, ZeroDivisionError, Exception) as e: return { 'success': False, 'message': str(e), 'result': None }
Kết quả:
{'success': False, 'message': 'division by zero', 'result': None}
Kết bài
Trong Python, ngoại lệ đóng vai trò quan trọng trong việc quản lý lỗi và duy trì sự ổn định của ứng dụng. Chúng là các đối tượng thuộc các lớp, mà hầu hết đều là các lớp con của lớp BaseException
. Việc xử lý ngoại lệ từ cụ thể đến tổng quát không chỉ giúp chúng ta nhận diện và khắc phục các lỗi chính xác hơn mà còn mang lại khả năng kiểm soát tốt hơn đối với cách ứng dụng phản ứng trước những tình huống bất ngờ. Bằng cách áp dụng các kỹ thuật xử lý ngoại lệ một cách hợp lý, bạn có thể tạo ra những ứng dụng bền vững, thân thiện và dễ sử dụng hơn cho người dùng. Hãy nhớ rằng, một lập trình viên giỏi không chỉ biết viết mã mà còn biết cách xử lý các lỗi khi chúng phát sinh, từ đó cải thiện chất lượng mã nguồn và trải nghiệm người dùng.