🌱 [Python] 16 - Python Exception Handling – Xử lý ngoại lệ trong Python

🌱 [Python] 16 - Python Exception Handling – Xử lý ngoại lệ trong Python

    Trong các ngôn ngữ lập trình nói chung và python nói riêng, việc xử lý các exception là vô cùng quan trọng, mục tiêu là tránh chương trình bị crash do các lỗi trong quá trình runtime!

1 - Exception Handling, Cấu trúc try-except và finally

    Trong Python, Exception Handling là cơ chế giúp chương trình không bị dừng đột ngột khi gặp lỗi, mà cho phép bạn kiểm soát và xử lý lỗi hợp lý để chương trình vẫn hoạt động ổn định.

print("Start")
x = 10 / 0
print("End")

    Kết quả:

ZeroDivisionError: division by zero

    → Chương trình dừng tại lỗi chia cho 0, dòng print("End") không được thực thi. Để tránh điều đó, ta dùng try–except.

Cấu trúc try-except và finally

    Cấu trúc cơ bản:

try:
    # đoạn code có thể gây lỗi
except SomeException:
    # xử lý lỗi
finally:
    # luôn chạy dù có lỗi hay không

    Ví dụ:

  1. try:
  2. num = int(input("Nhập số: "))
  3. print(10 / num)
  4. except ZeroDivisionError:
  5. print("Không thể chia cho 0!")
  6. except ValueError:
  7. print("Vui lòng nhập số hợp lệ!")
  8. finally:
  9. print("Kết thúc chương trình.")

    Trong đoạn code trên,

  • try: chứa code có khả năng gây lỗi.
  • except: xử lý loại lỗi cụ thể.
  • finally: luôn chạy, dù có lỗi hay không, thường dùng để đóng file, ngắt kết nối, ...

2 - Các Exception có sẵn trong Python

    Python có nhiều Exception mặc định, nếu sử dụng, khi có exception tương ứng xảy ra, chương trình sẽ thực thi nhanh except tương ứng. Dưới đây là một số lỗi phổ biến:

Exception Ý nghĩa
ZeroDivisionError Chia cho 0
ValueError Giá trị không hợp lệ
TypeError Kiểu dữ liệu không hợp lệ
IndexError Vượt quá chỉ số danh sách
KeyError Key không tồn tại trong dict
FileNotFoundError Không tìm thấy file

Python Built-in Exception

    Ví dụ:

try:
    lst = [1, 2, 3]
    print(lst[5])
except IndexError as e:
    print("Lỗi:", e)

# Lỗi: list index out of range

3 - Tạo Exception do người dùng định nghĩa

    Bạn có thể định nghĩa Exception riêng để xử lý các lỗi riêng biệt tùy mục đích người dùng.

  1. class NegativeNumberError(Exception):
  2. """Lỗi: số âm không hợp lệ"""
  3. pass

  4. def check_positive(x):
  5. if x < 0:
  6. raise NegativeNumberError("Giá trị âm không được phép!")
  7. print("Giá trị hợp lệ:", x)

  8. try:
  9. check_positive(-10)
  10. except NegativeNumberError as e:
  11. print("Lỗi người dùng:", e)

    Output:

Lỗi người dùng: Giá trị âm không được phép!

4 - raise – chủ động ném ngoại lệ

    Bạn có thể chủ động "ném lỗi" bằng lệnh raise để kiểm soát luồng chương trình.

  1. def divide(a, b):
  2. if b == 0:
  3. raise ZeroDivisionError("Không thể chia cho 0")
  4. return a / b

  5. try:
  6. print(divide(10, 0))
  7. except ZeroDivisionError as e:
  8. print("Lỗi:", e)

    Output:

Lỗi: Không thể chia cho 0

5 - Thực hành: Retry & Logging

    Tình huống thực tế: Gọi API hoặc xử lý dữ liệu mạng có thể thất bại tạm thời. Ta xây dựng một decorator retry tự động thử lại khi có lỗi, và ghi log mỗi lần lỗi.

  1. import time, random, logging

  2. logging.basicConfig(level=logging.INFO)

  3. class ConnectionError(Exception):
  4. pass

  5. def unstable_api_call():
  6. """Giả lập API lỗi ngẫu nhiên"""
  7. if random.random() < 0.7:
  8. raise ConnectionError("Mất kết nối API")
  9. return "✅ Kết nối thành công!"

  10. def retry(retries=3, delay=1):
  11. def decorator(func):
  12. def wrapper(*args, **kwargs):
  13. for i in range(retries):
  14. try:
  15. return func(*args, **kwargs)
  16. except Exception as e:
  17. logging.warning(f"Thử lần {i+1} thất bại: {e}")
  18. time.sleep(delay)
  19. raise Exception(f"Thất bại sau {retries} lần thử")
  20. return wrapper
  21. return decorator

  22. @retry(retries=3, delay=1)
  23. def call_api():
  24. return unstable_api_call()

  25. print(call_api())

    Output:

WARNING:root:Thử lần 1 thất bại: Mất kết nối API
✅ Kết nối thành công!

Kiểm thử bằng Unit Test

    Unit Test để kiểm tra decorator hoạt động đúng:

  1. import time
  2. import logging
  3. import unittest

  4. # -------------------------------------------------
  5. # Decorator: retry with exponential backoff + logging
  6. # -------------------------------------------------
  7. def retry(retries=3, delay=1, backoff=2, exceptions=(Exception,)):
  8. """
  9. Retry decorator with exponential backoff and logging.
  10. :param retries: number of retries
  11. :param delay: initial delay between retries
  12. :param backoff: multiplier applied to delay after each failure
  13. :param exceptions: tuple of exception types to catch
  14. """
  15. def decorator(func):
  16. def wrapper(*args, **kwargs):
  17. current_delay = delay
  18. for attempt in range(1, retries + 1):
  19. try:
  20. return func(*args, **kwargs)
  21. except exceptions as e:
  22. if attempt == retries:
  23. logging.error(f"All {retries} attempts failed.")
  24. raise
  25. logging.warning(f"Attempt {attempt} failed: {e}")
  26. time.sleep(current_delay)
  27. current_delay *= backoff
  28. return wrapper
  29. return decorator

  30. # -------------------------------------------------
  31. # Unit Tests
  32. # -------------------------------------------------
  33. class TestRetryDecorator(unittest.TestCase):

  34. def test_success_on_first_try(self):
  35. @retry(retries=3, delay=0.1)
  36. def always_succeed():
  37. return "success"
  38. self.assertEqual(always_succeed(), "success")

  39. def test_success_after_retries(self):
  40. counter = {'attempts': 0}
  41. @retry(retries=3, delay=0.1)
  42. def fail_twice_then_succeed():
  43. counter['attempts'] += 1
  44. if counter['attempts'] < 3:
  45. raise ValueError("Fake error")
  46. return "success"
  47. self.assertEqual(fail_twice_then_succeed(), "success")

  48. def test_all_retries_fail(self):
  49. @retry(retries=3, delay=0.1)
  50. def always_fail():
  51. raise RuntimeError("Always fail")
  52. with self.assertRaises(RuntimeError):
  53. always_fail()

  54. if __name__ == "__main__":
  55. unittest.main()

    Output:

Attempt 1 failed: Always fail
Attempt 2 failed: Always fail
Attempt 3 failed: Always fail
All 3 attempts failed.
.Attempt 1 failed: Fake error
..
---------------------------------------------------------------------- Ran 3 tests in 0.001s

OK

>>>>>> Follow ngay <<<<<<<

Để nhận được những bài học miễn phí mới nhất nhé 😊
Chúc các bạn học tập tốt 😊

Nguyễn Văn Nghĩa

Mình là một người thích học hỏi và chia sẻ các kiến thức về Nhúng IOT.

Đăng nhận xét

Mới hơn Cũ hơn
//
Avatar

Đăng nhập

Người dùng mới? Đăng ký ngay

hoặc

Bằng việc tạo tài khoản, bạn đồng ý với Chính sách bảo mật & Chính sách Cookie.