1.7 函式
隨著程式開始變大,我們會想要有條理地組織這些程式。本節簡要介紹函式、庫模組以及帶有異常的錯誤處理。
自定義函式對你要重用的程式碼使用函式。下面是函式的定義方式:
def sumcount(n): ''' Returns the sum of the first n integers ''' total = 0 while n > 0: total += n n -= 1 return total
函式呼叫:
a = sumcount(100)
函式是執行某些任務並返回結果的一系列語句。 return 關鍵字需要顯式指定函式的返回值。
庫函式Python 帶有一個大型的標準庫。使用 import 訪問庫模組。示例:
import mathx = math.sqrt(10)import urllib.requestu = urllib.request.urlopen('http://www.python.org/')data = u.read()
稍後,我們將更詳細地介紹庫和模組。
錯誤和異常函式將錯誤報告為異常。異常會導致函式中止,如果未處理,可能會導致整個程式終止。
在你的 Python 直譯器(REPL)中嘗試一下:
>>> int('N/A')Traceback (most recent call last):File "<stdin>", line 1, in <module>ValueError: invalid literal for int() with base 10: 'N/A'>>>
出於除錯的目的,上面的錯誤資訊描述了發生的情況,錯誤產生的位置以及回溯。該回溯顯示導致失敗的其它函式呼叫。
捕獲和處理異常異常可以被捕獲並處理。要捕獲異常,使用 try - except 語句:
for line in f: fields = line.split() try: shares = int(fields[1]) except ValueError: print("Couldn't parse", line) ...
該名稱 ValueError 必須與你嘗試捕獲的錯誤型別匹配。
通常,根據所執行的操作,很難提前確切地知道可能會發生哪種錯誤。不管是好是壞,通常會新增在程式意外崩潰後的異常處理(示例:”天哪,我們忘記捕獲錯誤了。我們應該處理錯誤的。“)。
觸發異常要觸發異常,請使用 raise 語句:
raise RuntimeError('What a kerfuffle')
這將導致程式因異常回溯而中止,除非該異常透過 try-except 程式碼塊捕獲。
% python3 foo.pyTraceback (most recent call last): File "foo.py", line 21, in <module> raise RuntimeError("What a kerfuffle")RuntimeError: What a kerfuffle
練習練習 1.29:定義一個函式嘗試定義一個簡單的函式:
>>> def greeting(name): 'Issues a greeting' print('Hello', name)>>> greeting('Guido')Hello Guido>>> greeting('Paula')Hello Paula>>>
如果函式的第一條語句是字串,那麼它被當做文件字串。嘗試輸入一個命令來顯示該文件字串,例如 help(greeting)。
練習 1.30:將指令碼轉換為函式把你在 練習1.27 為 pcost.py 程式編寫的程式碼放到 portfolio_cost(filename) 函數里面。此函式以檔名作為輸入,讀取檔案中的投資組合資料,把投資組合總的費用作為浮點數返回。
要使用你的函式,請修改程式,使其看起來像下面這樣:
def portfolio_cost(filename): ... # Your code here ...cost = portfolio_cost('Data/portfolio.csv')print('Total cost:', cost)
執行程式時,你應該會看到和以前一樣的輸出。執行程式後,你也可以輸入一下命令來互動式地呼叫函式:
bash $ python3 -i pcost.py
這將允許你從互動模式呼叫函式:
>>> portfolio_cost('Data/portfolio.csv')44671.15>>>
能夠互動地試驗程式碼對除錯和測試非常有用。
練習 1.31:錯誤處理如果你在缺少某些欄位的檔案上使用函式,會發生什麼情況?
>>> portfolio_cost('Data/missing.csv')Traceback (most recent call last): File "<stdin>", line 1, in <module> File "pcost.py", line 11, in portfolio_cost nshares = int(fields[1])ValueError: invalid literal for int() with base 10: ''>>>
在這一點上,你面臨一個決定:要使程式正常工作,你可以透過消除錯誤行(bad lines)來清理原始輸入檔案,或者修改程式碼,以某種方式處理錯誤行。
請修改 pcost.py 程式以捕獲異常,列印警告資訊然後繼續處理檔案餘下部分。
練習1.32:使用庫函式Python 帶有一個擁有大量有用函式的大型標準庫。csv 模組是一個在這裡可能有用的庫。無論何時,每當必須必須使用 CSV 資料檔案時,都應使用 csv 模組。下面是一個有關 csv 模組是如何工作的示例:
>>> import csv>>> f = open('Data/portfolio.csv')>>> rows = csv.reader(f)>>> headers = next(rows)>>> headers['name', 'shares', 'price']>>> for row in rows: print(row)['AA', '100', '32.20']['IBM', '50', '91.10']['CAT', '150', '83.44']['MSFT', '200', '51.23']['GE', '95', '40.37']['MSFT', '50', '65.10']['IBM', '100', '70.44']>>> f.close()>>>
csv 模組有一個非常棒的功能——它處理各種底層細節,例如引號和適當的逗號拆分。在上面的輸出中,你會注意到它從第一列的名稱(names)中刪除了雙引號。
修改你的 pcost.py 程式,以使用 csv 模組進行解析,然後嘗試執行前面的示例。
練習 1.33:從命令列讀取在 pcost.py 程式中,輸入檔案的名稱已經被硬編碼到程式碼中:
# pcost.pydef portfolio_cost(filename): ... # Your code here ...cost = portfolio_cost('Data/portfolio.csv')print('Total cost:', cost)
雖然用於學習和測試還行,但在實際的程式中,你可能不會這麼做。
相反,你可以把檔名作為引數傳遞給指令碼。嘗試按以下步驟修改程式的底部:
# pcost.pyimport sysdef portfolio_cost(filename): ... # Your code here ...if len(sys.argv) == 2: filename = sys.argv[1]else: filename = 'Data/portfolio.csv'cost = portfolio_cost(filename)print('Total cost:', cost)
sys.argv 是一個列表,該列表包含了在命令列上傳遞的引數(如果有)。
要執行程式,你需要從終端(terminal)執行 Python。
示例,從 Unix系統中的 bash 執行:
bash % python3 pcost.py Data/portfolio.csvTotal cost: 44671.15bash %