Python - Ngoại lệ Định nghĩa bởi Người dùng

Xin chào các nhà phép thuật Python tương lai! Hôm nay, chúng ta sẽ bắt đầu hành trình hấp dẫn vào thế giới các ngoại lệ do người dùng định nghĩa trong Python. Đừng lo nếu bạn mới bắt đầu học lập trình; tôi sẽ hướng dẫn bạn qua cuộc phiêu lưu này bước từng bước, như thế tôi đã làm cho hàng ngàn học viên trong những năm dạy học. Vậy hãy nắm chắc chiếc cây cơ hữu của bạn (bàn phím), và hãy bắt đầu!

Python - User-defined Exception

Ngoại lệ Định nghĩa bởi Người dùng trong Python

Trước khi bắt đầu tạo các ngoại lệ của riêng mình, hãy nhanh chóng tổng kết lại điều gì là ngoại lệ. Hãy tưởng tượng bạn đang nấu một bữa ăn ngon miệng, nhưng đột ngột bạn nhận ra mình đã hết nguyên liệu quan trọng. Đó tương đương như một ngoại lệ trong lập trình – là tình huống bất ngờ phá vỡ luồng thường xuyên của mã của bạn.

Python có rất nhiều ngoại lệ tích hợp sẵn như ValueError, TypeError, và ZeroDivisionError. Nhưng đôi khi, chúng ta cần tạo các ngoại lệ đặc biệt của riêng mình để xử lý các tình huống độc đáo trong chương trình của mình. Đó là nơi ngoại lệ do người dùng định nghĩa trở nên hữu ích!

Làm thế nào để Tạo một Ngoại lệ Định nghĩa bởi Người dùng

Tạo một ngoại lệ của riêng mình y như nấu một chiếc bánh (thật, một công thức bánh dễ dàng). Bạn chỉ cần tạo một lớp mới kế thừa từ lớp Exception tích hợp sẵn hoặc bất kỳ lớp con nào của nó. Hãy xem một ví dụ đơn giản:

class MySpecialError(Exception):
pass

Đó là tất! Bạn vừa tạo được ngoại lệ do người dùng đầu tiên của mình. Câu lệnh pass được sử dụng vì chúng ta không cần thêm bất kỳ chức năng bổ sung nào vào lớp ngoại lệ của mình.

Nhưng đòi hỏi chúng ta muốn ngoại lệ của mình có thêm thông tin? Hãy tạo một ngoại lệ khác:

class ValueTooLargeError(Exception):
def __init__(self, message, value):
self.message = message
self.value = value

Trong ví dụ này, chúng ta đã thêm phương thức __init__ vào lớp ngoại lệ của mình. Điều này cho phép chúng ta truyền thông tin bổ sung khi chúng ta nâng lên ngoại lệ.

Nâng lên Ngoại lệ Định nghĩa bởi Người dùng

Bây giờ khi chúng ta có các ngoại lệ tùy chỉnh của mình, hãy xem cách chúng ta có thể sử dụng chúng trong mã của mình. Nâng lên một ngoại lệ tương đương như gửi tín hiệu cảnh báo khi có điều gì đó không ổn. Dưới đây là cách bạn có thể làm điều đó:

def check_value(value):
max_value = 100
if value > max_value:
raise ValueTooLargeError("Giá trị quá lớn!", value)
print(f"Giá trị {value} hợp lệ.")

# Hãy thử nó
try:
check_value(150)
except ValueTooLargeError as error:
print(f"Oops! {error.message} Giá trị là {error.value}")

Trong ví dụ này, chúng ta đang kiểm tra xem một giá trị có quá lớn hay không. Nếu có, chúng ta nâng lên ngoại lệ ValueTooLargeError với một thông điệp tùy chỉnh và giá trị thực tế.

Xử lý Ngoại lệ Định nghĩa bởi Người dùng

Xử lý ngoại lệ do người dùng định nghĩa tương tự như xử lý các ngoại lệ tích hợp sẵn. Chúng ta sử dụng khối try-except đáng tin cậy. Hãy mở rộng ví dụ trước đó của chúng ta:

def process_value(value):
try:
check_value(value)
except ValueTooLargeError as error:
print(f"Lỗi: {error.message} Giá trị {error.value} không được phép.")
# Tại đây bạn có thể thêm mã để xử lý lỗi, như yêu cầu một giá trị mới
else:
print("Giá trị đã được xử lý thành công!")
finally:
print("Việc kiểm tra giá trị hoàn tất.")

# Hãy thử nó với các giá trị khác nhau
process_value(50)
process_value(200)

Trong đoạn mã này, chúng ta đang sử dụng khối try-except để xử lý ngoại lệ ValueTooLargeError. Chúng ta cũng đã thêm một đoạn else sẽ chạy nếu không có ngoại lệ nào được nâng lên, và một đoạn finally sẽ luôn chạy, bất kể có ngoại lệ xảy ra hay không.

Ví dụ Hoàn chỉnh

Bây giờ, hãy đặt tất cả lại trong một ví dụ phức tạp hơn. Tưởng tượng chúng ta đang tạo một hệ thống ngân hàng đơn giản:

class InsufficientFundsError(Exception):
def __init__(self, balance, amount):
self.balance = balance
self.amount = amount
self.message = f"Số dư không đủ. Số dư: ${balance}, Thử rút: ${amount}"

class NegativeAmountError(Exception):
def __init__(self, amount):
self.amount = amount
self.message = f"Không thể xử lý số tiền âm: ${amount}"

class BankAccount:
def __init__(self, balance=0):
self.balance = balance

def deposit(self, amount):
if amount < 0:
raise NegativeAmountError(amount)
self.balance += amount
print(f"Đặt cọc ${amount}. Số dư mới: ${self.balance}")

def withdraw(self, amount):
if amount < 0:
raise NegativeAmountError(amount)
if amount > self.balance:
raise InsufficientFundsError(self.balance, amount)
self.balance -= amount
print(f"Rút ${amount}. Số dư mới: ${self.balance}")

# Hãy sử dụng lớp BankAccount của chúng ta
account = BankAccount(100)

try:
account.deposit(50)
account.withdraw(30)
account.withdraw(200)  # Điều này nên nâng lên một InsufficientFundsError
except NegativeAmountError as error:
print(f"Lỗi: {error.message}")
except InsufficientFundsError as error:
print(f"Lỗi: {error.message}")
else:
print("Tất cả các giao dịch đã hoàn tất thành công.")
finally:
print(f"Số dư cuối cùng: ${account.balance}")

Trong ví dụ này, chúng ta đã tạo một lớp BankAccount với phương thức depositwithdraw. Chúng ta cũng đã định nghĩa hai ngoại lệ đặc biệt: InsufficientFundsErrorNegativeAmountError.

Khi chúng ta cố gắng rút nhiều tiền hơn số dư trong tài khoản, nó nâng lên một InsufficientFundsError. Nếu chúng ta cố gắng gửi hoặc rút một số tiền âm, nó nâng lên một NegativeAmountError.

Đây là một ví dụ tuyệt vời về cách các ngoại lệ do người dùng định nghĩa có thể làm cho mã của chúng ta trở nên dễ đọc hơn và giúp chúng ta xử lý các tình huống lỗi cụ thể một cách rõ ràng và tổ chức.

Kết luận

Xin chúc mừng! Bạn vừa nâng cấp kỹ năng Python của mình bằng cách học về các ngoại lệ do người dùng định nghĩa. Các ngoại lệ tùy chỉnh này như quân đội cá nhân của bạn trong việc bắt lỗi, sẵn sàng nhảy vào hành động khi có tình huống bất ngờ xảy ra trong mã của bạn.

Hãy nhớ, chìa khóa để đắc phong các ngoại lệ do người dùng định nghĩa là thực hành. Hãy thử tạo các ngoại lệ của riêng mình cho các tình huống khác nhau, và sớm bạn sẽ xử lý lỗi như một chuyên gia!

Dưới đây là bảng tóm tắt các phương thức chúng ta đã cover:

Phương thức Mô tả
class CustomError(Exception): Tạo một lớp ngoại lệ mới
raise CustomError() Nâng lên một ngoại lệ tùy chỉnh
try: Bắt đầu một khối try
except CustomError as error: Bắt một ngoại lệ tùy chỉnh cụ thể
else: Chạy nếu không có ngoại lệ nào được nâng lên
finally: Luôn chạy, bất kể có ngoại lệ xảy ra hay không

Chúc mãi mãi không có lỗi, và mọi ngoại lệ của bạn đều được bắt!

Credits: Image by storyset