Python – Exception

Các lỗi được phát hiện trong quá trình thực thi được gọi là exception. Trong quá trình thực thi script, có thể xảy ra lỗi. Ví dụ: không thể lưu file vì dung lượng ổ cứng đầy; kết nối Internet có thể bị ngắt và ứng dụng đang retry việc connect Internet. Tất cả những điều này có thể dẫn đến crash application đang chạy. Để xử lí những vấn đề này, chúng ta cần phải xử lí tất cả các case có thể gây lỗi. Trong Python, chúng ta sử dụng xử lí exception.

Catch exception trong Python

Trong Python, chúng ta sử dụng syntax sau để xử lí excecption

try:
   # do something

except ValueError:
   # handle ValueError exception

except (IndexError, ZeroDivisionError):
   # handle multiple exceptions
   # IndexError and ZeroDivisionError

except:
   # handle all other exceptions

finally:
   # cleanup resources

Code có thể có exception được đặt trong block ‘try’. Keyword ‘except’ bắt các exception trong chương trình. Block ‘finally’ luôn luôn được thực thi; code trong block ‘finally’ thường là để clean resource như close file, close database connection.

ZeroDivisionError

Không thể chia cho 0. Nếu xảy ra phép toán chia cho 0, trình thông dịch raise ‘ZeroDivisionError’, script bị interrupt.

Ví dụ 1: Exception khi chia cho 0

def input_numbers():

    a = float(input("Enter first number:"))
    b = float(input("Enter second number:"))
    return a, b


x, y = input_numbers()
print(f"{x} / {y} is {x/y}")

Trong ví dụ này, yêu cầu nhập vào 2 số float x, y. Sau đó in ra màn hình phép chia 2 số float. Nếu y = 0, trình thông dịch raise exception ZeroDivisionError

Kết quả:
C:\Users\Dell>python test.py
Enter first number:1
Enter second number:0
Traceback (most recent call last):
File “test.py”, line 9, in
print(f”{x} / {y} is {x/y}”)
ZeroDivisionError: float division by zero

Ví dụ 2: Sử dụng if/else để xử lí exception ở ví dụ 1

def input_numbers():
    a = float(input("Enter first number:"))
    b = float(input("Enter second number:"))
    return a, b


x, y = input_numbers()

while True:

    if y != 0:
        print(f"{x} / {y} is {x/y}")
        break

    else:
        print("Cannot divide by zero")
        x, y = input_numbers()

Ví dụ này check điều kiện y != 0 trước khi thực hiện phép chia, exception sẽ không xuất hiện, script không bị interrupted.

Kết quả:
C:\Users\Dell>python test.py
Enter first number:1
Enter second number:0
Cannot divide by zero
Enter first number:

Ví dụ 3: Sử dụng try/except để xử lí exception

def input_numbers():

    a = float(input("Enter first number:"))
    b = float(input("Enter second number:"))
    return a, b


x, y = input_numbers()


try:
    print(f"{x} / {y} is {x/y}")

except ZeroDivisionError:
    print("Cannot divide by zero")
    x, y = input_numbers()

Code thực hiện phép chia được đặt trong block try, except sẽ bắt exception ‘ZeroDivisionError’ nếu trình thông dịch raise exception ‘ZeroDivisionError’.

ValueError

Raise exception ‘ValueError’ khi built-in function nhận argument đúng kiểu dữ liệu nhưng giá trị không đúng. Xem ví dụ sau để hiểu rõ hơn.

Ví dụ 4: Validate giá trị truyền vào function

def read_age():
    age = int(input("Enter your age: "))

    if age < 0 or age > 130:
        raise ValueError("Invalid age")

    return age


try:
    val = read_age()
    print(f"Your age is {val}")

except ValueError as e:
    print(e)

function read_age() yêu cầu user nhập vào tuổi, kiểm tra giá trị tuổi, nếu age < 0 or age > 130 => developer “chủ động” raise exception ValueError với nội dung là “Invalid age”.

except ValueError as e:

Keyword ‘except’ bắt exception ‘ValueError’, e chứa message “Invalid age”.

Kết quả:
C:\Users\Dell>python test.py
Enter your age: -3
Invalid age

Python multiple exceptions

Có thể bắt nhiều loại exception trong cùng một keyword ‘except’
Ví dụ 5: Bắt nhiều exception trong cùng 1 keyword except

import os

try:
    os.mkdir('newdir')
    print('directory created')

    raise RuntimeError("Runtime error occurred")

except (FileExistsError, RuntimeError) as e:
    print(e)

Ví dụ này có thể bắt 2 exception: FileExistsError và RuntimeError

os.mkdir('newdir')

Tạo folder mới với function os.mkdir(). Nếu đường dẫn folder đã tồn tại, trình thông dịch auto raise FileExistsError.

raise RuntimeError("Runtime error occurred")

Developer raise exception ‘RuntimeError’ với message “Runtime error occurred”.

Kết quả:
Chạy lần đầu script
C:\Users\Dell>python test.py
[WinError 183] Cannot create a file when that file already exists: ‘newdir’

Chạy lần 2, đường dẫn ‘newdir’ đã được tạo, raise exception ‘FileExistsError’ và script interrupt luôn tại vị trí gọi hàm os.mkdir(‘newdir’).

C:\Users\Dell>python test.py
[WinError 183] Cannot create a file when that file already exists: ‘newdir’

Python exception argument

Exception có thể có giá trị cho biết detail nguyên nhân gây ra lỗi.

Ví dụ 6: Argument exception

try:
    a = (1, 2, 3, 4)
    print(a[5])

except IndexError as e:

    print(e)
    print("Class:", e.__class__)

Trong ví dụ này, in ra thông tin của lỗi thông qua argument exception, thông tin của lỗi được gán cho ‘e’.

Kết quả:
C:\Users\Dell>python test.py
tuple index out of range
Class:

Phân cấp của exception

Exception được tổ chức theo cấp, Exception là cha của tất các các exception.
Ví dụ 7: Phân cấp của exception

try:
    while True:
       pass

except KeyboardInterrupt:

    print("Program interrupted")

Script chạy vô hạn không kết thúc. Nếu chúng ta ấn Ctrl+C, vòng lặp vô hạn sẽ bị interrupt và trình thông dịch raise ‘KeyboardInterrupt’.

Kết quả:
C:\Users\Dell>python test.py
Program interrupted

Exception
BaseException
KeyboardInterrupt

Ví dụ 8: Bắt exception ở cấp cao hơn

try:
    while True:
        pass

except BaseException:
    print("Program interrupted")

except BaseException có thể bắt được keyword interrupt “Ctrl+C”. Tuy nhiên, nên chỉ định exception cụ thể trong câu lệnh except.

Kết quả:
C:\Users\Dell>python test.py
Program interrupted

User-defined exception

Chúng ta có thể tự define exception riêng theo ý mình bằng cách định nghĩa new class exception.
Ví dụ 9: Tự define exception

class BFoundEx(Exception):
    def __init__(self, value):
        self.par = value

    def __str__(self):
        return f"BFoundEx: b character found at position {self.par}"

string = "There are beautiful trees in the forest."

pos = 0

for i in string:
    try:
        if i == 'b':
            raise BFoundEx(pos)
        pos = pos + 1

    except BFoundEx as e:
        print(e)

Trong ví dụ này, chúng ta định nghĩa new exception class ‘BFoundEx’ kế thừa từ class ‘Exception’.

if i == 'b':
    raise BFoundEx(pos)

Nếu tìm thấy kí tự ‘b’ trong string, raise exception BFoundEx => khởi tạo instance của class ‘BFoundEx’, giá trị ‘pos’ được set cho self.par.

Kết quả:
C:\Users\Dell>python test.py
BFoundEx: b character found at position 10

Keyword finally

Keyword ‘finally’ luôn luôn được thực hiện. Code đặt trong block ‘finally’ thường được dùng để clean resource như close file, close socket,…

Ví dụ 10: Keyword ‘finally’

f = None

try:

    f = open('data1.txt', 'r')
    contents = f.readlines()

    for line in contents:
        print(line.rstrip())

except IOError:
    print('Error opening file')

finally:
    print('Close file')
    if f:
        f.close()

Ví dụ này thực hiện open file ‘data.txt’. Nếu không open được file, trình thông dịch raise exception ‘IOError’. Nếu mở được file, thực hiện close file trong block ‘finally’. Đây là cấu trúc common khi lập trình với database.

Kết quả:
Nếu file tồn tại
C:\Users\Dell>python test.py
Today is 28/5/2022,
I don’t want to do anything because I’m tired
Close file

Nếu file không tồn tại
C:\Users\Dell>python test.py
Error opening file
Close file

Backtrace

Backtrace show call stack (stack của các function được gọi cho tới điểm đó) tại thời điểm xảy ra exception. Module ‘traceback’ cung cấp function có thể extract, format và print stack trace của chương trình Python.

Ví dụ 11: In ra Backtrace khi xảy ra exception

import traceback


def myfun():
    def myfun2():
        try:
            3 / 0
        except ZeroDivisionError as e:

            print(e)
            print("Class:", e.__class__)

            for line in traceback.format_stack():
                print(line.strip())

    myfun2()

def test():
    myfun()

test()

Trong ví dụ này, exception xảy ra trong function myfun2() lồng trong function myfun().

for line in traceback.format_stack():

Function format_stack() extract raw backtrace từ stack frame và định dạng chúng thành list tuple và duyệt list tuple.

Kết quả:
C:\Users\Dell>python test.py
division by zero
Class:
File “test.py”, line 21, in
test()
File “test.py”, line 19, in test
myfun()
File “test.py”, line 16, in myfun
myfun2()
File “test.py”, line 13, in myfun2
for line in traceback.format_stack():

Be the first to comment

Leave a Reply