目錄 | 上一節 (1.6 檔案) | 下一節 (2.0 處理資料)
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 math
x = math.sqrt(10)
import urllib.request
u = 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.py
Traceback (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.py
def portfolio_cost(filename):
...
# Your code here
...
cost = portfolio_cost('Data/portfolio.csv')
print('Total cost:', cost)
雖然用於學習和測試還行,但在實際的程式中,你可能不會這麼做。
相反,你可以把檔名作為引數傳遞給指令碼。嘗試按以下步驟修改程式的底部:
# pcost.py
import sys
def 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.csv
Total cost: 44671.15
bash %