10 自訂函數

其實我們已經開始使用函數了,例如我們請 Python 回傳資料類型的 type() 或者產生數列 list 的 range() 都是函數。函數的使用方法是在小括號中放輸入(input),然後呼叫函數得到輸出(output)。

10.1 查詢函數的用法

如果我們不知道某個函數的用法,可以使用 help() 查詢,了解一個函數的輸入與它可以設定的參數,例如查詢 range() 函數就知道可以指定產出數列的起始值(start 預設為 0)、終止值(stop)與公差(step 預設為 1):

help(range)

10.2 自訂函數

Python 自訂函數的架構:

def function_name(輸入1, 輸入2, 參數 1, 參數 2, ...):
    """
    Docstrings
    """
    # 主要的程式
    return 結果

首先給函數取個名字(function_name),接著在小括號中放進輸入(inputs)與參數(parameters),然後附上一段說明(Docstrings),在縮排內撰寫我們主要的程式,最後把輸出結果放在 return 後面。

10.2.1 函數說明文件 Docstrings

養成在自訂函數中加入 Docstrings 說明的好習慣,這有助於分享我們的自訂函數給其他人使用。Docstrings 可以區分為單行與多行:

def function_name(輸入, 參數 1, 參數 2, ...):
    """單行的 Docstring"""
    # 主要的程式
    return 結果
def function_name(輸入, 參數 1, 參數 2, ...):
    """
    多行的 Docstrings
    更多的說明內容
    """
    # 主要的程式
    return 結果

我們撰寫的 Docstrings 會在使用 help() 查詢函數用法的時候出現,例如我們的第一個自訂函數,它的作用是將輸入的數字平方之後回傳,我們就叫它 my_squared() 函數。

def my_squared(x):
    """
    將輸入的數字平方之後回傳
    """
    return x**2
help(my_squared) # 查詢 my_squared() 函數
## Help on function my_squared in module __main__:
## 
## my_squared(x)
##     將輸入的數字平方之後回傳

10.2.2 計算圓面積的函數

熟能生巧,我們來練習自訂一個輸入半徑長度可以計算出圓面積的函數叫做 circle_area()

def circle_area(r):
    """
    輸入半徑長度可以計算出圓面積
    """
    pi = 3.14
    return  pi * r**2
print(circle_area(3))
## 28.26

10.2.3 計算圓周長的函數

我們接著來練習自訂一個輸入半徑長度可以計算出圓周長的函數叫做 circle_circum()

def circle_circum(r):
    """
    輸入半徑長度可以計算出圓周長
    """
    pi = 3.14
    return 2 * pi * r
print(circle_circum(3))
## 18.84

10.2.4 加入參數

我們把前面兩個自訂函數的功能整合起來到一個函數裡面,這個時候我們會正式納入參數(parameters)的觀念到這個自訂函數中,使用一個布林參數 is_area 來讓使用者在呼叫函數時決定要計算圓面積或者圓周長。

def circle_calculator(r, is_area=True):
    """
    輸入半徑長度可以計算出圓面積與圓周長
    利用參數 is_area 決定要回傳圓面積或圓周長
    預設回傳圓面積
    """
    pi = 3.14
    if is_area == True:
        return pi * r**2
    else:
        return 2 * pi * r
print(circle_calculator(3))
## 28.26
print(circle_calculator(3, is_area=False))
## 18.84

這裡我們設計 is_area 參數是有一個預設值 True,假如自訂函數時沒有指定 is_area 參數的預設值並且呼叫函數時也沒有指定,就會出現錯誤訊息:

def circle_calculator(r, is_area): # 沒有指定預設值
    """
    輸入半徑長度可以計算出圓面積與圓周長
    利用參數 is_area 決定要回傳圓面積或圓周長
    預設回傳圓面積
    """
    pi = 3.14
    if is_area == True:
        return pi * r**2
    else:
        return 2 * pi * r

print(circle_calculator(3)) # 沒有指定 is_area 參數的值

10.2.5 有多個輸出的函數

我們再修正一下 circle_calculator() 這個自訂函數,讓它可以輸入半徑就一併輸出圓面積與圓周長。這時我們只要在 return 敘述後面將兩個輸出用逗號 , 隔開就會儲存在一個 tuple 中回傳。

def circle_calculator(r):
    """
    輸入半徑長度可以計算出圓面積與圓周長
    將圓面積與圓周長一起回傳
    """
    pi = 3.14
    area = pi * r**2
    circum = 2 * pi * r
    return area, circum
print(circle_calculator(3))
## (28.26, 18.84)

10.3 變數範圍

在自訂函數時必須意識到變數範圍這個議題,程式中的變數開始被我們區分為區域變數(local variables)全域變數(global variables),在函數內我們能夠使用兩種類型的變數,但是在函數外,僅能使用全域變數。用講的很抽象,我們還是動手定義自訂函數來釐清這個概念,用一個自訂函數 my_squared() 來演釋:

def my_squared(x):
    ans = x ** 2 # ans 是一個區域變數
    return ans

print(ans) # 在函數外沒有 ans

假如我們將 ans 在函數外也進行指派:

ans = 0 # ans 是一個全域變數
def my_squared(x):
    ans = x ** 2 # ans 是一個區域變數
    return ans
print(my_squared(3)) # 區域變數 ans 的值是 9
## 9
print(ans) # 全域變數 ans 的值是 0
## 0

10.4 匿名函數(lambda 函數)

除了正常的函數自訂結構,我們也可以使用一個稱作 lambda 函數的寫法,這樣的寫法不需要 return 敘述,我們來試著將 my_squared() 函數改寫成 lambda 函數:

my_squared = lambda x : x ** 2
print(my_squared(3))

lambda 函數常與 map()filter() 一起使用,像是利用 filter() 搭配 lambda 函數將 1 到 10 的偶數挑出來:

filter(lambda x : x % 2 == 0, range(1, 11))

或者是利用 map() 搭配 lambda 函數將 1 到 10 的每一個數字都平方:

map(lambda x : x**2, range(1, 11))

10.5 錯誤處理

自訂函數時如果能夠掌握某些特定錯誤,撰寫客製的錯誤訊息,可以讓使用自訂函數的使用者更快完成偵錯,我們可以使用 try - except 的語法結構進行錯誤處理。

我們修改原本計算平方數的 my_squared() 當使用者輸入文字時會回傳客製錯誤訊息:「請輸入數值。」

def my_squared(x):
    """
    計算平方數且具有錯誤處理的函數
    """
    try:
        return x ** 2
    except:
        return "請輸入數值。"
print(my_squared("3"))
## 請輸入數值。

除了我們在這個例子中遭遇的 TypeError 以外,也可以針對不同的錯誤類型設計客製錯誤訊息,接著我們再寫一個除法的函數 divide() 來示範,如果使用者輸入的資料類型錯誤(TypeError)就回傳「請輸入數值。」假如使用者在除數指定了 0(ZeroDivisionError)就回傳「除數不可以為零。」

def divide(x, y):
    try:
        return x / y
    except TypeError:
        return "請輸入數值。"
    except ZeroDivisionError:
        return "除數不可以為零。"
    except:
        return "其他的錯誤。"
print(divide("9", 3))
## 請輸入數值。
print(divide(9, 0))
## 除數不可以為零。