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ệ

8. 1. Lỗi cú pháp

Lỗ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 "", line 1
    while True print('Hello world')
                   ^
SyntaxError: invalid syntax

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 "", line 1, in 
ZeroDivisionError: division by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
  File "", line 1, in 
TypeError: Can't convert 'int' object to str implicitly
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 "", line 1, in 
ZeroDivisionError: division by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
  File "", line 1, in 
TypeError: Can't convert 'int' object to str implicitly

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

  • Đầu tiên, mệnh đề try ((các) câu lệnh giữa từ khóa và từ khóa) được thực thi

  • Nếu không có ngoại lệ xảy ra, mệnh đề ngoại trừ bị bỏ qua và việc thực thi câu lệnh kết thúc

  • Nếu một ngoại lệ xảy ra trong khi thực hiện mệnh đề try, phần còn lại của mệnh đề sẽ bị bỏ qua. Sau đó, nếu loại của nó khớp với ngoại lệ được đặt tên theo từ khóa, mệnh đề ngoại trừ được thực thi và sau đó việc thực thi tiếp tục sau câu lệnh

  • Nếu một ngoại lệ xảy ra không khớp với ngoại lệ có tên trong mệnh đề ngoại trừ, nó sẽ được chuyển sang các câu lệnh bên ngoài;

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):
...     pass
4 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):
...     pass
7 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)
...

('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs

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 "", line 1, in 
NameError: HiThere

Đố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 "", line 1, in 
ZeroDivisionError: division by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
  File "", line 1, in 
TypeError: Can't convert 'int' object to str implicitly
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 "", line 1, in 
ZeroDivisionError: division by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
  File "", line 1, in 
TypeError: Can't convert 'int' object to str implicitly
1

8. 5. Ngoại lệ do người dùng định nghĩa

Cá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 "", line 1, in 
ZeroDivisionError: division by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
  File "", line 1, in 
TypeError: Can't convert 'int' object to str implicitly
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ẹp

Câ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 "", line 1, in 
ZeroDivisionError: division by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
  File "", line 1, in 
TypeError: Can't convert 'int' object to str implicitly
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])
    raise
0 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])
    raise
0 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ệ

  • Nếu một ngoại lệ xảy ra trong quá trình thực hiện mệnh đề

    >>> while True:
    ..     try:
    ..         x = int(input("Please enter a number: "))
    ..         break
    ..     except ValueError:
    ..         print("Oops!  That was no valid number.  Try again...")
    ...
    
    4, ngoại lệ đó có thể được xử lý bởi một mệnh đề. Nếu ngoại lệ không được xử lý bởi mệnh đề
    >>> while True:
    ..     try:
    ..         x = int(input("Please enter a number: "))
    ..         break
    ..     except ValueError:
    ..         print("Oops!  That was no valid number.  Try again...")
    ...
    
    6, thì ngoại lệ sẽ được đưa ra 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])
        raise
    
    0 đã được thực thi

  • Một ngoại lệ có thể xảy ra trong quá trình thực hiện mệnh đề

    >>> while True:
    ..     try:
    ..         x = int(input("Please enter a number: "))
    ..         break
    ..     except ValueError:
    ..         print("Oops!  That was no valid number.  Try again...")
    ...
    
    6 hoặc
    ... except (RuntimeError, TypeError, NameError):
    ...     pass
    
    7. Một lần nữa, ngoại lệ được đưa ra 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])
        raise
    
    0 đã được thực thi

  • Nếu 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 đạt đến , hoặc câu lệ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])
        raise
    
    0 sẽ thực hiện ngay trước khi câu lệnh
    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()
    
    3,
    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()
    
    4 hoặc
    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()
    
    5 được thực thi

  • Nếu 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])
        raise
    
    0 bao gồm câu lệnh
    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()
    
    5, thì giá trị được trả về sẽ là giá trị từ câu lệnh
    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()
    
    5 của 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])
        raise
    
    0, không phải giá trị từ câu lệnh ________9____5 của mệnh đề ________3____4

Ví dụ

>>> 10 * (1/0)
Traceback (most recent call last):
  File "", line 1, in 
ZeroDivisionError: division by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
  File "", line 1, in 
TypeError: Can't convert 'int' object to str implicitly
4

Một ví dụ phức tạp hơn

>>> 10 * (1/0)
Traceback (most recent call last):
  File "", line 1, in 
ZeroDivisionError: division by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
  File "", line 1, in 
TypeError: Can't convert 'int' object to str implicitly
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])
    raise
0 đã đượ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ước

Mộ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 "", line 1, in 
ZeroDivisionError: division by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
  File "", line 1, in 
TypeError: Can't convert 'int' object to str implicitly
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 "", line 1, in 
ZeroDivisionError: division by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
  File "", line 1, in 
TypeError: Can't convert 'int' object to str implicitly
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