VIII.
Các kỹ thuật đặc biệt trong
Python:
1.
Quản lí lỗi:
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.
Lỗi 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 ngoại 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, NameError và TypeError.
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 ngoại 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 ngoại lệ do người sử dụng định nghĩa:
Các
chương trình có thể đặt tên ngoại
lệ của chúng bằng cách tạo ra một
lớp ngoại lệ mới. Các ngoại lệ thông thường được thừa kế từ lớp
Exception, hoặc là kế thừa trực
tiếp hoặc không trực 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ỏ.
Hãy like nếu bài viết có ích →
Kết bạn với gisgpsrs trên Facebook
để nhận bài viết mới nóng hổi
Không có nhận xét nào:
Đăng nhận xét