翻譯:《實用的Python程式設計》01_07_Functions

codists發表於2021-02-19

目錄 | 上一節 (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.27pcost.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 %

目錄 | 上一節 (1.6 檔案) | 下一節 (2.0 處理資料)

注:完整翻譯見 https://github.com/codists/practical-python-zh

相關文章