Lessons XI : Các kỹ thuật đặc biệt trong Python

VIII. Các k thut đc bit trong Python:

1. Qun lí li:

        Trong phần này, sẽ tìm hiểu về các cơ chế quản lí lỗi trong Python. Bao gồm các lỗi cú pháp và cơ chế dùng ngoại lệ (exception).

a. Li cú pháp:

Bản chất là lỗi phân tích từ trong Python, đây có lẽ là lỗi phổ biến nhất của những người mới học lập trình Python. Một ví dụ lỗi đơn giản:

 
>>> while True print 'Hello world'
  File "<stdin>", line 1, in ?
    while True print 'Hello world'
                   ^
SyntaxError: invalid syntax

Việc phân tích lặp lại trên các dòng mắc lỗi và hiện thị một con trỏ đầu tiên tại vị trí lỗi được dò ra (kiểu tương tự như pascal). Lỗi gây ra (được dò thấy đầu tiên) có dấu hiệu con trỏở trước, như trong ví dụ trên, lỗi dò thấy là tại từ khóa print, vì nó thiếu dấu hai chấm (“:”) ở trước. Tên file và số dòng cũng được in ra để có thể biết chính xác trong trường hợp đầu vào là một script.

 

b. Các ngoi l (Exception):

        Mặc dù một câu lệnh hoặc một biểu thức là chính xác về cú pháp nhưng nó vẫn có thể gây ra lỗi khi chạy nó. Các lỗi dò được trong quá trình thực hiện được gọi là các ngoại lệ (exception) và không thể tránh khỏi tuyệt đối các lỗi này. Phần sau, sẽ tiếp tục nghiên cứu về cách điều khiển nó trong Python. Hầu hết các ngoại lệ đều không điều khiển được bởi chương trình, tuy nhiên các có thể chỉ ra các thông báo lỗi:

>>> 10 * (1/0)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ZeroDivisionError: integer division or modulo by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: cannot concatenate 'str' and 'int' objects

Dòng  cuối cùng của thông báo lỗi cho biết chuyện gì đã xảy ra. Các ngoại lệ có nhiều kiểu khác nhau, và các kiểu này được in ở cuối thông báo lỗi. Trong ví dụ trên là:ZeroDivisionError, NameErrorTypeError.  Các xâu in ra là các kiểu ngoại lệ và tên của các ngoại lệ được xây dựng sẵn đưa ra. Điều này là được áp dụng cho tất cả các ngoại lệ cài sẵn, nhưng không áp dụng cho các ngoại lệ người sử dụng tự định nghĩa. Các tên ngoại lệ chuẩn các định danh có sẵn (không trùng với các từ khóa dành riêng).

Phần còn lại của thông báo cung cấp chi tiết dựa trên kiểu của ngoại lệ và nguyên nhân gây ra lỗi. Phần đặt trước thông báo lỗi chỉ ra ngữ cảnh ngoại lệ đó xảy ra, theo cơ chế dò lùi kiểu ngăn xếp. Tóm lại, nó chứa một danh sách các dòng nguồn, tuy nhiên có sẽ không hiển thị các dòng đọc từ đầu vào chuẩn.

c. Điều khiển các ngoại lệ:

          Có thể viết các chương trình để điều khiển các ngoại lệ nào đó, Xem xét ví dụ dưới đây, yêu cầu người sử dụng nhập vào dữ liệu cho đến khi là số nguyên, nhưng cho phép người sử dụng ngắt chương trình (sử dụng CTRL + C hoặc tương tự do hệ điều hành hỗ trợ). Chú ý rằng các ngắt do người sử dụng tạo ra được nhận dạng bởi các ngoại lệ do ngắt bàn phím (KeyboardInterrupt exeption)

>>> while True:
...     try:
...         x = int(raw_input("Please enter a number: "))
...         break
...     except ValueError:
...         print "Oops!  That was no valid number.  Try again..."
...

Câu lệnh try hoạt động như sau:

  • Đầu tiên, mệnh đề try (câu lệnh giữa try và từ khóa except được thực hiện).
  • Nếu không có ngoại lệ nào xảy ra, mệnh đề except được bỏ qua và tiếp tục thực hiện try cho đến khi kết thúc.
  • Nếu có một ngoại lệ xảy ra trong khi đang thực hiện mệnh đề try thì phần còn lại của mệnh đề sẽ bị bỏ qua. Sau đó nếu như loại lỗi đó mà tương ứng với tên của ngoại lệ được đặt sau từ khóa except thì mệnh đề except sẽ được thực hiện, và sau đó tiếp tục thực hiện cho đến hết câu lệnh try.
  • Nếu một ngoại lệ xảy ra mà không tương ứng với ngoại lệ đã được đặt sau mệnh đề except thì nó sẽ bỏ qua thoát ra khỏi câu lệnh try. Nếu không có điều khiển nào được tìn thấy, thì đó một ngoại lệ không điều khiển và quá trình thực hiện kết thúc với một thông báo như đã chỉ ra ở trên.

Một câu lệnh try có thể có nhiều hơn một mệnh đề except, để xác định các điểu khiển cho các ngoại lệ khác nhau. Toàn bộ một điều khiển sẽ được thực hiện. Các điều khiển chỉ điều khiển các ngoại lệ xảy ra tương ứng với mệnh đề try. Một mệnh đề except có thể cỏ nhiều ngoại lệ theo sau được đặt trong dấu ngoặc đơn như một bộ, ví dụ:

... except (RuntimeError, TypeError, NameError):
...     pass

Mệnh đề excpet cuối cùng có thể bỏ qua tên của ngoại lệ, để đáp ứng như một kí tự đại diện. Sử dụng điều này phải hết sức cẩn thận, bởi vì rất dễ bỏ qua một lỗi chương trình thực sự. Nó cũng có thể được sử dụng để in ra một thông báo lỗi và sau đó tái đề xuất ngoại lệ đó:

import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except IOError, (errno, strerror):
    print "I/O error(%s): %s" % (errno, strerror)
except ValueError:
    print "Could not convert data to an integer."
except:
    print "Unexpected error:", sys.exc_info()[0]

raise

Câu lệnh try… except có một mệnh đề else mở rộng, nó được xuất hiện sau tất cả các mệnh đề except. Nó được sử dụng để viết các đoạn mã mà chỉ thi hành khi mệnh đề try không gây ra một ngoại lệ nào. Ví dụ:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print 'cannot open', arg
    else:
        print arg, 'has', len(f.readlines()), 'lines'
        f.close()

Sử dụng mệnh đề else giúp ngăn ngừa được các ngoaik lệ gây ra bởi đoạn code nằm trong try…

Khi một ngoại lệ xảy ra, nó có thể có một giá trị liên quan, được gọi là đối số của ngoại lệ. Cách biểu diễn và kiểu của đối số đó phụ thuộc vào kiểu ngoại lệ. Mệnh đề except có thể xác định một biến theo sau tên ngoại lệ. Biến đó được gắn với một trường hợp ngoại lệ với các đối số được lưu trong instance.args. Để thuận tiện, trường hợp ngoại lệ đó định nghĩa __getitem__and__str__vì thế các đối số đó có thể được truy nhập hoặc đưa ra trực tiếp không cần tham chiếu .args

>>> try:
...    raise Exception('spam', 'eggs')
... except Exception, inst:
...    print type(inst)     # the exception instance
...    print inst.args      # arguments stored in .args
...    print inst           # __str__ allows args to printed directly
...    x, y = inst          # __getitem__ allows args to be unpacked directly
...    print 'x =', x
...    print 'y =', y
...
<type 'instance'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs

            Nếu một ngoại lệ có một đối số, nó được đưa ra như phần cuối cùng của một thông báo cho các ngoại lệ không điều khiển.
            Các điều khiển ngoại lệ không chỉ điều khiển các ngoại lệ xảy ra ngay lập tức trong mệnh đề try, mà còn cho cả các lỗi xảy ra trong các hàm mà được gọi trong mệnh đề try. Ví dụ:
>>> def this_fails():
...     x = 1/0
... 
>>> try:
...     this_fails()
... except ZeroDivisionError, detail:
...     print 'Handling run-time error:', detail
... 
Handling run-time error: integer division or modulo by zero

d. Đưa ra các ngoi l:

Câu lệnh raise cho phép người lập trình tác động đến một ngoại lệ xác định để cho nó xảy ra. Ví dụ:

>>> raise NameError, 'HiThere'
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: HiThere

Đối số đầu tiên tạo ra các tên ngoại lệ sẽ được sinh ra. Đối số thứ hai xác định đối số của ngoại lệ đó. Một cách thay thế khác, câu lệnh ở trên có thể được viết lại. Nếu cần xác định một ngoại lệ được đưa ra nhưng không có ý định điều khiển nó, một dạng đơn giản của câu lệnh raise cho phép tái đưa ra một ngoại lệ:

>>> try:
...     raise NameError, 'HiThere'
... except NameError:
...     print 'An exception flew by!'
...     raise
...
An exception flew by!
Traceback (most recent call last):
  File "<stdin>", line 2, in ?
NameError: HiThere

e. Các ngoi l do người s dng đnh nghĩa:

          Các chương trình có th đt tên ngoi l ca chúng bng cách to ra mt lp ngoi l mi. Các ngoi l thông thường được tha kế t lp Exception, hoc là kế tha trc tiếp hoc không trc tiếp. Ví d:

      >>> class MyError(Exception):
...     def __init__(self, value):
...         self.value = value
...     def __str__(self):
...         return repr(self.value)
... 
>>> try:
...     raise MyError(2*2)
... except MyError, e:
...     print 'My exception occurred, value:', e.value
... 
My exception occurred, value: 4
>>> raise MyError, 'oops!'
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
__main__.MyError: 'oops!'

Trong ví dụ này, phương thức mặc định __init__ của lớp Exception bị ghi chèn lên. Một thuộc tính giá trị mới được tạo ra, nó sẽ thay thế cách thức mặc định tạo ra thuộc tính args.

Các lớp ngoại lệ có thể được định nghĩa để làm bất cứ điều gì như các lớp thông thường, nhưng thường là nó được xây dựng rất đơn giản, nó chỉ đưa ra một số các thuộc tính thông tin về các lỗi được lấy ra từ bởi các điều khiển đối với các ngoại lệ. Khi tạo ra một module mà có thể gây ra một vài lỗi phân biệt, một thói quen phổ biến là tạo ra một lớp cơ sở cho các ngoại lệ được định nghĩa bởi module đó, và lướp con để tạo ra các lớp ngoại lệ cụ thể cho các điều kiện lỗi khác nhau:

class Error(Exception):
    """Base class for exceptions in this module."""
    pass

class InputError(Error):
    """Exception raised for errors in the input.

    Attributes:
        expression -- input expression in which the error occurred
        message -- explanation of the error
    """

    def __init__(self, expression, message):
        self.expression = expression
        self.message = message

class TransitionError(Error):
    """Raised when an operation attempts a state transition that's not
    allowed.

    Attributes:
        previous -- state at beginning of transition
        next -- attempted new state
        message -- explanation of why the specific transition is not allowed
    """

    def __init__(self, previous, next, message):
        self.previous = previous
        self.next = next
        self.message = message

          Hầu hết các ngoại lệ do người sử dụng tạo ra được định nghĩa với các tên mà kết thúc là ‘Error’ giống với tên của các ngoại lệ chuẩn.
          Nhiều module chuẩn định nghĩa các ngoại lệ của riêng nó để thông báo các lỗi có thể xảy ra trong các hàm mà chúng định nghĩa. 

2. Quản lí bộ nhớ:
            Pyhton có một cơ chế tự động quản lý bộ nhớ (đếm số lần tham chiếu của hầu hết các đối tượng và tập hợp không hợp lệ để loại trừ chúng theo chu kì). Bộ nhớ sẽ được giải phóng ngay sau khi tham chiếu cuối cùng tới nó được loại bỏ.

 

  1. Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: