Xử lý ngoại lệ (Exception handling)
Đã giới thiệu| English | Ý nghĩa |
|---|---|
| exception/ekˈsepʃn/ | Lỗi ngoại lệ |
| exception handling/ekˈsepʃn ˈhændlɪŋ/ | Xử lý lỗi ngoại lệ |
| raise/reɪz/ | Ném (lỗi) |
Kiểm tra không thể viết code
- Một chương trình mở một file. Trước khi mở, bạn có thể kiểm tra xem file có tồn tại không, có đọc được không, ổ đĩa có đang hoạt động không. Giả sử mọi kiểm tra đều thành công.
- Giữa lần kiểm tra cuối cùng và lúc mở, người dùng rút thanh USB ra. File đã tồn tại khi bạn hỏi và không tồn tại khi bạn mở, và không có kiểm tra nào trước đó có thể lấp đầy khoảng trống đó.
- Một số lỗi đơn giản là không thể ngăn chặn, vì thế giới thay đổi giữa lúc kiểm tra và lúc thực thi. Điều mà một chương trình có thể làm là phản hồi lại chúng khi chúng xảy ra.
- Bài học này là ngoại lệ, cấu trúc TRY xử lý một trường hợp, và vị trí trong chương trình nơi ngoại lệ nên được bắt.
Ngoại lệ là gì và tại sao cần xử lý
- Một ngoại lệ là một lỗi xảy ra trong quá trình thực thi: chia cho số không, không tìm thấy tệp, đầu vào không hợp lệ, hoặc mạng đã mất kết nối.
- Xử lý ngoại lệ cho phép chương trình phát hiện lỗi và phản hồi theo cách có kiểm soát, thay vì bị sập và làm mất dữ liệu của người dùng.
- Các chương trình thực tế phải đối mặt với những lỗi không thể ngăn chặn trước, như sơ đồ minh họa. Và nếu không có ngoại lệ, mỗi thao tác sẽ cần một phép kiểm tra
IFriêng biệt bao quanh nó, khiến thuật toán thực tế bị chìm trong các bài kiểm tra lỗi. - Lợi ích thứ ba là lợi ích mà đề thi thường hỏi nhất: nó tách biệt luồng hoạt động bình thường khỏi việc xử lý lỗi, giúp phần chính dễ đọc hơn.
Một ngoại lệ là:
Difficulty 1/5 · 100 coins + a small bonus on first completion today
Ngoại lệ là các vấn đề thời gian chạy (chia cho 0, không tìm thấy tệp) mà xử lý ngoại lệ giúp bạn phản hồi một cách thanh lịch.
Tại sao sử dụng xử lý ngoại lệ thay vì kiểm tra trước mọi lỗi? Chọn tất cả các đáp án áp dụng.
Difficulty 2/5 · 140 coins + a small bonus on first completion today
Lợi ích là tính chính xác và sự rõ ràng. Tốc độ không phải là lợi ích; việc xử lý thêm một chút tài nguyên này là đáng bỏ ra.
Cấu trúc TRY
TRY
OPENFILE "data.txt" FOR READ
READFILE "data.txt", line
CLOSEFILE "data.txt"
EXCEPT FileNotFound
OUTPUT "Sorry, the file does not exist."
EXCEPT ReadError
OUTPUT "Sorry, there was an error reading the file."
FINALLY
OUTPUT "Finished attempting to read."
ENDTRY
- Khối TRY chứa mã có thể gây lỗi. Nếu chạy thành công không có lỗi, các khối EXCEPT sẽ bị bỏ qua hoàn toàn.
- Nếu xảy ra ngoại lệ, chương trình chuyển ngay sang khối EXCEPT khớp đầu tiên. Phần còn lại của khối TRY không được thực thi.
- Khối FINALLY luôn được thực thi dù có hay không có ngoại lệ xảy ra, khiến nó là nơi thích hợp để xử lý dọn dẹp như đóng tệp tin.

Một đường đi khi mọi thứ suôn sẻ, một đường khác khi có sự cố, và một đường luôn chạy
Cách xử lý ngoại lệ vận hành
Trình tự những gì xảy ra khi mã thất bại. Ngoại lệ nhảy ra khỏi luồng bình thường sang một trình xử lý, FINALLY làm sạch dù có hay không có lỗi, và chương trình tiếp tục thay vì sập.
Gắn mỗi từ khóa xử lý ngoại lệ với công việc của nó.
Difficulty 2/5 · 140 coins + a small bonus on first completion today
TRY bảo vệ đoạn code rủi ro, EXCEPT bắt lỗi, FINALLY dọn dẹp dù sao cũng vậy, RAISE ném một lỗi để được bắt.
Một khối FINALLY:
Difficulty 1/5 · 100 coins + a small bonus on first completion today
FINALLY luôn chạy, khiến nó lý tưởng cho việc dọn dẹp như đóng tệp.
Khối chạy dù có xảy ra ngoại lệ hay không, khiến nó thích hợp cho việc đóng tệp, là ____.
Difficulty 1/5 · 100 coins + a small bonus on first completion today
Việc dọn dẹp phải diễn ra trên cả hai đường dẫn. Việc đặt CLOSEFILE chỉ trong khối TRY nghĩa là nó bị bỏ qua đúng vào thời điểm lỗi khiến tệp mở chưa đóng.
Ví dụ minh họa: theo dõi luồng thực thi
- Trong mã trên, tệp tin không tồn tại. Hãy nêu chính xác những gì được in ra và những gì bị bỏ qua.
OPENFILEgây ra FileNotFound, nên việc thực thi rời khỏi khối TRY ngay lập tức: dòngREADFILEvàCLOSEFILEkhông bao giờ chạy.- Khối EXCEPT
FileNotFoundđược thực thi, in ra "Sorry, the file does not exist." KhốiReadErrorkhông chạy, vì chỉ bộ xử lý khớp đầu tiên được sử dụng. - Khối FINALLY được thực thi, in ra "Finished attempting to read."
- Mất điểm phổ biến nhất là không nhận thấy phần còn lại của khối TRY đã bị bỏ cuộc.
Tệp trong khối TRY không tồn tại. Xếp thứ tự những gì sẽ xảy ra.
Difficulty 3/5 · 200 coins + a small bonus on first completion today
Chỉ bộ xử lý phù hợp đầu tiên chạy, và FINALLY chạy dù sao cũng vậy. Phần còn lại bị bỏ qua của khối TRY là dấu mấu chốt dễ bị bỏ sót nhất.
Ném (Raising) một ngoại lệ
- Một thủ tục phát hiện ra vấn đề mà nó không thể giải quyết một cách hợp lý có thể ném một ngoại lệ, chuyển giao trách nhiệm cho người gọi:
IF b = 0 THEN RAISE DivideByZero
- Đây là thiết kế đúng đắn khi thủ tục biết có điều sai trái nhưng không biết phải làm gì. Thủ tục chia她知道 số chia bằng 0; chỉ người gọi mới biết nên yêu cầu người dùng nhập lại, dùng giá trị mặc định, hay bỏ tính toán.
Một thủ tục con sử dụng RAISE để:
Difficulty 1/5 · 100 coins + a small bonus on first completion today
RAISE ném một ngoại lệ lên người gọi, người đó có thể bắt nó bằng EXCEPT.
Tại sao một thủ tục chia lại RAISE ngoại lệ thay vì tự xử lý việc chia cho 0?
Difficulty 1/5 · 100 coins + a small bonus on first completion today
Hỏi người dùng lại, dùng giá trị mặc định, hoặc bỏ cuộc tính toán: chỉ có mã gọi mới có ngữ cảnh để lựa chọn.
Nơi xử lý ngoại lệ
- Xử lý ngoại lệ gần với nơi xảy ra khi phản hồi đơn giản và cục bộ: in thông báo, dùng giá trị mặc định, yêu cầu người dùng thử lại.
- Xử lý ở mức cao hơn khi quyết định thuộc về một phần lớn hơn của chương trình: liệu có hủy toàn bộ giao dịch, hoàn tác thay đổi, hay thông báo cho người dùng thao tác thất bại hay không.
- Quy tắc chung: bắt nó ở mức độ có đủ thông tin để quyết định phải làm gì, chứ không phải ở mức độ đầu tiên phát hiện ra.
Ví dụ minh họa: những điều không nên làm
- Một học sinh viết
EXCEPT: (do nothing)bao quanh cả chương trình để nó không bao giờ bị sập. Giải thích tại sao đây là thói quen kém. - Ngoại lệ bị nuốt chửng: chương trình tiếp tục như thể chẳng có chuyện gì xảy ra, nên nó tiếp tục với dữ liệu thiếu hoặc không hợp lệ và tạo ra kết quả sai thay vì thất bại rõ ràng.
- Thất bại im lặng khó chẩn đoán hơn một vụ sập, vì không có thông báo và không có dấu hiệu nào cho biết nó xảy ra ở đâu.
- Bắt mọi ngoại lệ ở một nơi cũng có nghĩa là mất loại lỗi cụ thể, nên không thể chọn phản hồi hợp lý. Bộ xử lý nên bắt một ngoại lệ cụ thể và thực sự phản hồi với nó.
Im lặng "nuốt chửng" một ngoại lệ (bắt nó nhưng không làm gì) che giấu các lỗi thực sự và khiến việc sửa lỗi khó khăn — bạn nên xử lý nó hoặc ít nhất ghi log lại.
Difficulty 1/5 · 100 coins + a small bonus on first completion today
Một bộ xử lý trống che giấu chính những vấn đề bạn cần tìm; hãy luôn phản hồi hoặc ghi nhận lỗi.
Điểm số dễ mất
- Khi có ngoại lệ, phần còn lại của khối TRY bị bỏ qua. Hãy nói điều đó khi theo dõi.
- Chỉ có EXCEPT khớp đầu tiên được chạy, không phải tất cả chúng.
- FINALLY luôn chạy, dù có hay không có lỗi, và đó là lý do khiến nó phù hợp để đóng tệp tin.
- Đừng bắt ngoại lệ rồi không làm gì. Lỗi bị nuốt chửng còn tệ hơn cả vụ sập, vì chương trình tiếp tục với dữ liệu xấu.
Bạn đã nắm vững
- một ngoại lệ là lỗi thời gian chạy; xử lý nó cho phép chương trình phản ứng thay vì sập, che chắn các lỗi không thể ngăn chặn trước, và tách biệt mã bình thường với xử lý lỗi
- TRY chứa mã rủi ro; khi có lỗi, phần còn lại bị bỏ qua và EXCEPT khớp đầu tiên chạy; FINALLY chạy trong cả hai trường hợp, nên việc dọn dẹp nằm ở đó
- một thủ tục không thể quyết định phản hồi thì nên NÉM ngoại lệ cho người gọi của nó
- xử lý gần đó cho phản hồi cục bộ đơn giản, cao hơn khi quyết định cần cái nhìn rộng hơn; đừng bao giờ bắt nó và không làm gì