1. 程式人生 > 其它 >零基礎學程式設計023:用with實現優雅地釋放資源

零基礎學程式設計023:用with實現優雅地釋放資源

在《零基礎學程式設計022:函式的世界》中我們寫了一個函式,通過訪問新浪的實時行情服務,得到股票的開盤價。

import urllib.request as req

def price(stock) :
    url = 'http://hq.sinajs.cn/list=' + stock
    with req.urlopen(url) as f :
        hq = f.read().decode('GBK')
        v = hq.split(',')
        return v[1]

新手對 with 的用法不太理解,如果以前學過C#語言,這個with類似於using

關鍵詞。先來看看不太好的寫法吧:

import urllib.request as req

def price(stock) :
    url = 'http://hq.sinajs.cn/list=' + stock
    f = req.urlopen(url)   # 儘量不要用這種寫法!
    hq = f.read().decode('GBK')
    v = hq.split(',')
    f.close() # 當前面發生異常時,不一定能夠執行到這條語句
    return v[1]

上面這段程式碼沒有用 with ... as ... 的寫法,而是用賦值語句把req.urlopen(url)賦給了 f,在返回開盤價 return v[1] 之前呼叫了 f.close() 把網路連線關閉。

在絕大多數情況下,這種程式碼不會有什麼問題。但這裡的程式碼訪問了網路,而訪問網路會有各種異常情況,比如網絡卡被禁用、WIFI未連線、無法連線網際網路、網路地址無效、代理設定不正確、網路伺服器故障、防火牆阻擋等等,這些異常都是程式設計之前無法完全預料的。

我們呼叫 urlopen() 打開了一個網路連線,在最後務必要保證把它關閉,即呼叫close() 函式。但當網路已經發生異常了,此時還未執行到close() 函式,程式就已經異常退出了,所以網路連線可能仍處於開啟狀態。

一般的小程式,這少量的未關閉的網路連線並不會造成什麼麻煩,有時作業系統還會在程序關閉時自動釋放這些連線,但如果編寫服務端程式時,幾秒鐘之內就可能產生數千個併發連線,當這種問題積累到一定程度後,程式就會出現莫名其妙的錯誤,而且這種錯誤特別難定位。

我在2002年用java寫過一個網路資訊釋出系統,當時有人的程式碼裡沒有正確地釋放Oracle資料庫連線,當正式上線時,幾分鐘之內產生了數百個未釋放的資料庫連線,Oralce主資料庫差點宕掉,幸好我們及時地把程式摘掉,才避免了一次事故的發生。

所以學習程式設計時,一定要參考別人的例子程式碼,尤其是參考官方的例子程式碼。網上流傳的一些核心程式碼只是為了說明具體的用法,寫法上並不規範,也沒有加入異常處理的相關程式碼,而真正產品級的程式碼,會加上許多邊界條件檢查、異常判斷的語句,從而讓產品更加健壯。

小結:

  • with 語句用於保證一些資源(檔案、網路連線、資料庫等)在發生異常時能夠正常地關閉或釋放
  • 程式設計初期就養成良好地程式設計習慣,將錯誤扼殺在搖籃裡
  • with 語句內部會自動呼叫close()語句釋放網路連線,其背後還有比較複雜的實現機制,以後再說

--- END ---