Vượt qua con trăn thất bại
Cho đến nay các thông báo lỗi vẫn chưa được đề cập nhiều, nhưng nếu bạn đã thử các ví dụ thì có thể bạn đã thấy một số. Có (ít nhất) hai loại lỗi có thể phân biệt được. lỗi cú pháp và ngoại lệ Show 8. 1. Lỗi cú phápLỗi cú pháp, còn được gọi là lỗi phân tích cú pháp, có lẽ là loại phàn nàn phổ biến nhất mà bạn nhận được khi vẫn đang học Python >>> while True print('Hello world') File " Trình phân tích cú pháp lặp lại dòng vi phạm và hiển thị một 'mũi tên' nhỏ chỉ vào điểm sớm nhất trong dòng mà lỗi được phát hiện. Lỗi là do (hoặc ít nhất là được phát hiện tại) mã thông báo trước mũi tên. trong ví dụ này, lỗi được phát hiện tại hàm , do dấu hai chấm ( >>> 10 * (1/0) Traceback (most recent call last): File "9) bị thiếu trước nó. Tên tệp và số dòng được in để bạn biết tìm ở đâu trong trường hợp đầu vào đến từ tập lệnh 8. 2. Ngoại lệNgay cả khi một câu lệnh hoặc biểu thức đúng về mặt cú pháp, nó vẫn có thể gây ra lỗi khi cố gắng thực hiện nó. Lỗi được phát hiện trong quá trình thực thi được gọi là ngoại lệ và không gây tử vong vô điều kiện. bạn sẽ sớm học cách xử lý chúng trong các chương trình Python. Tuy nhiên, hầu hết các trường hợp ngoại lệ không được xử lý bởi các chương trình và dẫn đến các thông báo lỗi như được hiển thị ở đây >>> 10 * (1/0) Traceback (most recent call last): File " Dòng cuối cùng của thông báo lỗi cho biết điều gì đã xảy ra. Các ngoại lệ có nhiều loại khác nhau và loại này được in như một phần của thông báo. các loại trong ví dụ là , và. Chuỗi được in dưới dạng loại ngoại lệ là tên của ngoại lệ tích hợp đã xảy ra. Điều này đúng với tất cả các ngoại lệ có sẵn, nhưng không nhất thiết phải đúng với các ngoại lệ do người dùng xác định (mặc dù đó là một quy ước hữu ích). Tên ngoại lệ tiêu chuẩn là số nhận dạng tích hợp (không phải từ khóa dành riêng) Phần còn lại của dòng cung cấp chi tiết dựa trên loại ngoại lệ và nguyên nhân gây ra nó Phần trước của thông báo lỗi hiển thị bối cảnh xảy ra ngoại lệ, ở dạng truy nguyên ngăn xếp. Nói chung, nó chứa một dòng nguồn liệt kê truy nguyên ngăn xếp; liệt kê các ngoại lệ tích hợp và ý nghĩa của chúng 8. 3. Xử lý ngoại lệCó thể viết các chương trình xử lý các ngoại lệ đã chọn. Hãy xem ví dụ sau, ví dụ này yêu cầu người dùng nhập dữ liệu cho đến khi nhập một số nguyên hợp lệ, nhưng cho phép người dùng ngắt chương trình (sử dụng Control-C hoặc bất kỳ hệ điều hành nào hỗ trợ); >>> while True: .. try: .. x = int(input("Please enter a number: ")) .. break .. except ValueError: .. print("Oops! That was no valid number. Try again...") ... Tuyên bố hoạt động như sau
Một câu lệnh có thể có nhiều hơn một mệnh đề ngoại trừ, để chỉ định các trình xử lý cho các ngoại lệ khác nhau. Nhiều nhất một trình xử lý sẽ được thực thi. Trình xử lý chỉ xử lý các trường hợp ngoại lệ xảy ra trong mệnh đề try tương ứng, không phải trong các trình xử lý khác của cùng một câu lệnh >>> while True: .. try: .. x = int(input("Please enter a number: ")) .. break .. except ValueError: .. print("Oops! That was no valid number. Try again...") ...4. Ví dụ, một mệnh đề ngoại trừ có thể đặt tên cho nhiều ngoại lệ dưới dạng một bộ được đặt trong ngoặc đơn ... except (RuntimeError, TypeError, NameError): ... pass Một lớp trong một mệnh đề tương thích với một ngoại lệ nếu nó là cùng một lớp hoặc một lớp cơ sở của nó (nhưng không phải là ngược lại - một mệnh đề except liệt kê một lớp dẫn xuất không tương thích với một lớp cơ sở). Ví dụ: đoạn mã sau sẽ in B, C, D theo thứ tự đó class B(Exception): pass class C(B): pass class D(C): pass for cls in [B, C, D]: try: raise cls() except D: print("D") except C: print("C") except B: print("B") Lưu ý rằng nếu các mệnh đề ngoại trừ bị đảo ngược (với ... except (RuntimeError, TypeError, NameError): ... pass4 trước), nó sẽ in ra B, B, B — mệnh đề ngoại trừ khớp đầu tiên được kích hoạt Mệnh đề ngoại trừ cuối cùng có thể bỏ qua (các) tên ngoại lệ, để dùng làm ký tự đại diện. Sử dụng điều này hết sức thận trọng, vì rất dễ che giấu một lỗi lập trình thực sự theo cách này. Nó cũng có thể được sử dụng để in một thông báo lỗi và sau đó kích hoạt lại ngoại lệ (cho phép người gọi cũng xử lý ngoại lệ đó) ________số 8_______ Câu lệnh … có một mệnh đề tùy chọn khác, khi xuất hiện, phải tuân theo tất cả các mệnh đề ngoại trừ. Nó hữu ích cho mã phải được thực thi nếu mệnh đề thử không đưa ra ngoại lệ. Ví dụ for arg in sys.argv[1:]: try: f = open(arg, 'r') except OSError: print('cannot open', arg) else: print(arg, 'has', len(f.readlines()), 'lines') f.close() Việc sử dụng mệnh đề ... except (RuntimeError, TypeError, NameError): ... pass7 tốt hơn là thêm mã bổ sung vào mệnh đề vì nó tránh vô tình bắt gặp một ngoại lệ không được đưa ra bởi mã đang được bảo vệ bởi câu lệnh >>> while True: .. try: .. x = int(input("Please enter a number: ")) .. break .. except ValueError: .. print("Oops! That was no valid number. Try again...") ...4 … >>> while True: .. try: .. x = int(input("Please enter a number: ")) .. break .. except ValueError: .. print("Oops! That was no valid number. Try again...") ...6 Khi một ngoại lệ xảy ra, nó có thể có một giá trị liên quan, còn được gọi là đối số của ngoại lệ. Sự hiện diện và loại đối số phụ thuộc vào loại ngoại lệ Mệnh đề ngoại trừ có thể chỉ định một biến sau tên ngoại lệ. Biến được liên kết với một thể hiện ngoại lệ với các đối số được lưu trữ trong class B(Exception): pass class C(B): pass class D(C): pass for cls in [B, C, D]: try: raise cls() except D: print("D") except C: print("C") except B: print("B")1. Để thuận tiện, thể hiện ngoại lệ xác định để các đối số có thể được in trực tiếp mà không cần phải tham chiếu đến class B(Exception): pass class C(B): pass class D(C): pass for cls in [B, C, D]: try: raise cls() except D: print("D") except C: print("C") except B: print("B")3. Người ta cũng có thể khởi tạo một ngoại lệ trước khi nâng nó lên và thêm bất kỳ thuộc tính nào vào nó như mong muốn >>> try: .. raise Exception('spam', 'eggs') .. except Exception as inst: .. print(type(inst)) # the exception instance .. print(inst.args) # arguments stored in .args .. print(inst) # __str__ allows args to be printed directly, .. # but may be overridden in exception subclasses .. x, y = inst.args # unpack args .. print('x =', x) .. print('y =', y) ... Nếu một ngoại lệ có đối số, chúng sẽ được in ở phần cuối cùng ('chi tiết') của thông báo cho các ngoại lệ chưa được xử lý Trình xử lý ngoại lệ không chỉ xử lý ngoại lệ nếu chúng xuất hiện ngay trong mệnh đề try mà còn nếu chúng xuất hiện bên trong các hàm được gọi (thậm chí gián tiếp) trong mệnh đề try. Ví dụ >>> def this_fails(): .. x = 1/0 ... >>> try: .. this_fails() .. except ZeroDivisionError as err: .. print('Handling run-time error:', err) ... Handling run-time error: division by zero 8. 4. Tăng ngoại lệCâu lệnh cho phép lập trình viên buộc một ngoại lệ cụ thể xảy ra. Ví dụ >>> raise NameError('HiThere') Traceback (most recent call last): File " Đối số duy nhất để chỉ ra ngoại lệ được nêu ra. Đây phải là một thể hiện ngoại lệ hoặc một lớp ngoại lệ (một lớp bắt nguồn từ ). Nếu một lớp ngoại lệ được thông qua, nó sẽ được khởi tạo hoàn toàn bằng cách gọi hàm tạo của nó mà không có đối số >>> 10 * (1/0) Traceback (most recent call last): File "0 Nếu bạn cần xác định xem một ngoại lệ đã được đưa ra nhưng không có ý định xử lý nó hay không, thì một dạng câu lệnh đơn giản hơn sẽ cho phép bạn đưa ra lại ngoại lệ đó >>> 10 * (1/0) Traceback (most recent call last): File "1 8. 5. Ngoại lệ do người dùng định nghĩaCác chương trình có thể đặt tên cho các ngoại lệ của riêng mình bằng cách tạo một lớp ngoại lệ mới (xem thêm về các lớp Python). Các ngoại lệ thường được bắt nguồn từ lớp, trực tiếp hoặc gián tiếp Các lớp ngoại lệ có thể được định nghĩa để làm bất cứ điều gì mà bất kỳ lớp nào khác có thể làm, nhưng thường được giữ đơn giản, thường chỉ cung cấp một số thuộc tính cho phép trình xử lý trích xuất thông tin về lỗi cho ngoại lệ. Khi tạo một mô-đun có thể phát sinh một số lỗi riêng biệt, một thực tế phổ biến là tạo một lớp cơ sở cho các ngoại lệ được xác định bởi mô-đun đó và lớp con để tạo các lớp ngoại lệ cụ thể cho các điều kiện lỗi khác nhau >>> 10 * (1/0) Traceback (most recent call last): File "2 Hầu hết các ngoại lệ được xác định với các tên kết thúc bằng “Lỗi”, tương tự như cách đặt tên cho các ngoại lệ tiêu chuẩn Nhiều mô-đun tiêu chuẩn xác định các ngoại lệ của riêng chúng để báo cáo lỗi có thể xảy ra trong các chức năng mà chúng xác định. Thông tin thêm về các lớp học được trình bày trong chương 8. 6. Xác định hành động dọn dẹpCâu lệnh có một mệnh đề tùy chọn khác nhằm xác định các hành động dọn dẹp phải được thực hiện trong mọi trường hợp. Ví dụ >>> 10 * (1/0) Traceback (most recent call last): File "3 Nếu có một mệnh đề, thì mệnh đề import sys try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except OSError as err: print("OS error: {0}".format(err)) except ValueError: print("Could not convert data to an integer.") except: print("Unexpected error:", sys.exc_info()[0]) raise0 sẽ thực thi như tác vụ cuối cùng trước khi câu lệnh hoàn thành. Mệnh đề import sys try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except OSError as err: print("OS error: {0}".format(err)) except ValueError: print("Could not convert data to an integer.") except: print("Unexpected error:", sys.exc_info()[0]) raise0 chạy cho dù câu lệnh >>> while True: .. try: .. x = int(input("Please enter a number: ")) .. break .. except ValueError: .. print("Oops! That was no valid number. Try again...") ...4 có tạo ra ngoại lệ hay không. Các điểm sau thảo luận về các trường hợp phức tạp hơn khi xảy ra ngoại lệ
Ví dụ >>> 10 * (1/0) Traceback (most recent call last): File "4 Một ví dụ phức tạp hơn >>> 10 * (1/0) Traceback (most recent call last): File "5 Như bạn có thể thấy, mệnh đề được thực hiện trong mọi trường hợp. Việc tăng bằng cách chia hai chuỗi không được xử lý bởi mệnh đề và do đó được tăng lại sau khi mệnh đề import sys try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except OSError as err: print("OS error: {0}".format(err)) except ValueError: print("Could not convert data to an integer.") except: print("Unexpected error:", sys.exc_info()[0]) raise0 đã được thực thi Trong các ứng dụng trong thế giới thực, mệnh đề này rất hữu ích để giải phóng các tài nguyên bên ngoài (chẳng hạn như tệp hoặc kết nối mạng), bất kể việc sử dụng tài nguyên có thành công hay không 8. 7. Hành động dọn dẹp được xác định trướcMột số đối tượng xác định các hành động dọn dẹp tiêu chuẩn sẽ được thực hiện khi đối tượng không còn cần thiết, bất kể thao tác sử dụng đối tượng thành công hay thất bại. Hãy xem ví dụ sau, ví dụ này sẽ cố mở một tệp và in nội dung của nó ra màn hình >>> 10 * (1/0) Traceback (most recent call last): File "6 Vấn đề với mã này là nó để tệp mở trong một khoảng thời gian không xác định sau khi phần mã này thực thi xong. Đây không phải là vấn đề trong các tập lệnh đơn giản, nhưng có thể là vấn đề đối với các ứng dụng lớn hơn. Câu lệnh cho phép các đối tượng như tệp được sử dụng theo cách đảm bảo chúng luôn được dọn dẹp kịp thời và chính xác >>> 10 * (1/0) Traceback (most recent call last): File "7 Sau khi câu lệnh được thực thi, tệp f luôn được đóng, ngay cả khi gặp sự cố khi xử lý các dòng. Các đối tượng, chẳng hạn như tệp, cung cấp các hành động dọn dẹp được xác định trước sẽ chỉ ra điều này trong tài liệu của chúng |