Tuesday, 3 February 2015

[Python] try...finally

Trong python, try...except là cặp đôi hoàn cảnh mà đi đâu cũng sẽ gặp, mà nếu như trong 1, 2 trang code của bạn không dùng try...except một lần nào thì có vẻ như đoạn code ấy có vấn đề.
finally không được sử dụng nhiều như except, và vì thế người dùng có thể không hiểu rõ tác dụng của nó. Bài viết này sẽ giải thích các trường hợp đặc biệt với finally.

Từ khoá:
  • try statement : câu lệnh try
  • finally clause: mệnh đề finally
Định nghĩa về "try"
The try statement specifies exception handlers and/or cleanup code for a group of statements
Vậy try... có 2 tác dụng:
  1. xử lý exception (exception handling - bằng mệnh đề except)
  2. và cleanup code cho một tập các câu lệnh (bằng mệnh đề finally)
Chi tiết về việc sử dụng exception là một phần không thể thiếu / là văn hoá của Python rồi nên ở đây không bàn tới.

try:
...
except ... :
...
else:
...
finally:
...
Code trong try sẽ chạy, nếu có exception nào xảy ra và phần except bắt được thì phần code trong khối except đó sẽ chạy, nếu không có exception nào thì code trong else (nếu có) sẽ chạy. Cuối cùng là đến code trong finally chạy.
CODE TRONG ``finally`` LUÔN ĐƯỢC CHẠY.
Một số điều cần lưu ý:
- khi có exception ở bất cứ mệnh đề nào và nó không được xử lý thì:
exception này sẽ được Python lưu tạm thời,
mệnh đề finally sẽ được chạy, đến CUỐI của đoạn code trong finally, nếu Python kiểm tra thấy có exception đang được lưu tạm thời nó sẽ raise lại exception này nếu: không có
  1. exception mới sinh bởi code nằm trong khối finally.
  2. câu lệnh return hoặc break được chạy trong finally.
(chú ý phần code trong finally sẽ không thể truy cập tới thông tin của exception đã lưu).

Ví dụ với file hihi.py dưới đây:
# coding: utf-8
def pika():
    try:
        1/0
    finally:
        print 'Chả có gì xảy ra, lêu lêu'
        return

try:
    pika()
except Exception:
    print 'Không một ai động tới ~.~'

$ python /tmp/hihi.py
Chả có gì xảy ra, lêu lêu
Ví dụ với break xem ở đây.
- ở python2, trong mệnh đề ``finally`` không được chứa câu lệnh ``continue``.
In [5]: try:
   ...:     1/0
   ...: finally:
   ...:     continue
   ...:
  File "<ipython-input-5-ba5a318d1708>", line 4
    continue
SyntaxError: 'continue' not supported inside 'finally' clause

- giá trị return của 1 function được quyết định bởi câu lệnh return chạy sau cùng. 
KỲ LẠ? không phải return là function sẽ kết thúc sao?
Về cơ bản là vậy nhưng nếu câu lệnh return đó nằm trong khối try...finally thì câu lệnh return đầu tiên chạy trong finally sẽ là giá trị trả về của function.
Dòng return trong try vẫn được chạy, nhưng bởi vì code trong finally luôn được chạy, nên câu return trong finally mới là giá trị được trả về, python quyết định thế, thắc mắc thì lên phường:

In [3]: def meo():
   ...:     try:
   ...:         return 'try'
   ...:     finally:
   ...:         return 'finally'
   ...:         return 'chẳng bao giờ xảy ra đâu'
   ...:

In [4]: meo()
Out[4]: 'finally'
Chốt lại cuối cùng: mệnh đề finally LUÔN LUÔN CHẠY.
Hãy kiểm tra xem bạn có hiểu bài viết không bằng cách làm bài tập sau:
def output_test():
    for i in xrange(3):
        try:
            if i % 2 == 1:
                continue
            else:
                print i
        finally:
            print 'Trong finally'

try:
    thử đưa ra kết quả của output_test() mà không cần chạy code
    chạy thử đoạn code
except KếtQuảSai:
    print "Đọc lại từ đầu"
finally:
    print "Chúc mừng, hết"
    print "By hvn@familug.org"
Tài liệu: Python 2 reference