<style> .red { color: red; } .blue{ color: blue; } .green{ color: green; } </style> # 函式 (Function) ## 5-1 認識函式 - 函式(Function)是<span class='red'>將一段具有某種功能或重複使用的statement寫成獨立的程式</span>。 - 然後給予名稱以供後續呼叫使用,這樣可以簡化程式提高可讀性 - 有時我們也稱函式(Function)為**方法(method)**, **程序(procedure)** 或**副程式(subroutine)** :::success 使用函式(Function): - 優點: <span class='red'>重複使用性</span>使程式會變得比較精簡、<span class='red'>可讀性提高</span>(容易理解有利於偵錯) - 缺點:因為多了一道呼叫的程式,執行的速度自然比直接將statement寫進程式裡面慢一點 ::: ## 5-2 定義函式 ```python= def FunctionName([Parameters]): statements [return|ReturnValues] ``` :::success 1. 若不需要return值回去,則可以省略return statement 2. 省略return statement。<span class='red'>嚴格上來說並不是沒有回傳值,而是回傳一個None的特殊值</span> 3. 若有多個return value則以逗號(,)相隔 ::: ## 5-3 呼叫函式 - 要注意函式的參數(parameter),再帶入參數時順序不能用錯 - <span class='red'>當直譯器讀取到該行程式碼為函式時,會先將這幾行statement存放在記憶體,暫時不執行</span> - 當直譯器讀取到呼叫的函式時,控制權會轉移到該函式,等該函式執行完後,才會將控制權再轉回呼叫函式的的地方 :::success 1. <span class='red'>當程式中有多個函式定義時,Python並沒有規定這些函式定義的前後順序,只要在呼叫某個函式時,其函式定義已經存放在記憶體即可。</span> 2. 我們將函式定義中的參數稱為「形式參數」(formal parameter) 或「參數」(parameter)。 3. 而函式呼叫中的參數稱為「實際參數」(actual parameter) 或「引數」(argument)。 ::: ## 5-4 函式的參數 ### 5-4-1 參數的傳遞方式 ### 傳值呼叫(Call by Value) - Python並不允許程式設計人員選擇參數傳遞的方式。 - 當參數屬於不可改變內容的物件(數值、字串、tuple)時,會採取**傳值呼叫(Call by Value)**。 - <span class='red'>此時函式無法改變參數的值,因為傳遞給函式的是參數的值,而不是參數的位址。</span> ```python= def swap(a,b): temp = a a = b b = temp x, y = 1, 2 print(x, y) swap(x, y) print(x, y) ``` >Output: >> 1 2 1 2 :::success 1. 由於變數x, y為數值,<span class='red'>屬於不可改變內容的物件</span>,因此上方程式碼第9行屬於傳值呼叫(Call by Value)。 2. 因為a, b參數和x, y變數是不同物件,被交換的是a, b參數,而x, y變數的值不受影響。 ::: ### 傳址呼叫(Call by Reference) - 相反的,當參數屬於可改變內容的物件(list, set, dict)時,會採取**傳址呼叫(Call by Reference)**。 - <span class='red'>此時函式就可以改變參數的值,因為傳遞給函式的是參數的位址,而不是參數的值。</span> ```python= def swap(a): temp = a[0] a[0] = a[1] a[1] = temp x = [1, 2] print(x[0], x[1]) swap(x) print(x[0], x[1]) ``` >Output: >> 1 2 2 1 :::success 1. 由於變數x為list,<span class='red'>屬於可改變內容的物件</span>,因此上方程式碼第9行屬於傳址呼叫(Call by Reference)。 2. 因為a參數和x變數是相同物件(參照相同位址),當a參數值被交換了,x變數的值也會有影響。 ::: ### 5-4-2 關鍵字引述 - Python預設採取<span class='red'>位置引述(Position Argument),函式呼叫裡面的引數順序必須對應函式定義裡面的參數順序</span>,一旦寫錯順序,會導致對應錯誤。 - 但有些參數順序實在不好記,此時可以使用<span class='red'>關鍵字引數(keyword argument)來做區分,也就是在呼叫函式時指定引數所對應的參數名稱</span>。 ```python= def trapezoidArea(top, bottom, height): area = (top + bottom) * height / 2 print("梯形面積為:", area) trapezoidArea(10, 20, 5) trapezoidArea(10, height = 5, bottom = 20) trapezoidArea(height = 5, bottom = 20, top = 10) ``` >Output: >>梯形面積為: 75.0 梯形面積為: 75.0 梯形面積為: 75.0 ### 5-4-3 預設引述值 - 我們可以在定義函式時設定預設引數值(default argument value),這樣當函式呼叫裡面沒有提供某個引數時,就會採取預設引數值。 - 這種擁有預設引數值的引數稱為**選擇性引數(optional argument)**,必須放在一般引數的後面。 ```Python= def teaTime(dessert, drink = "紅茶"): print("我的甜點是", dessert, ",甜點是", drink) teaTime("馬卡龍", "咖啡") teaTime("帕尼尼") teaTime(drink = "奶茶", dessert = "三明治") teaTime("紅豆餅", drink = "綠茶") ``` >Output: >> 我的甜點是 馬卡龍 ,甜點是 咖啡 我的甜點是 帕尼尼 ,甜點是 紅茶 我的甜點是 三明治 ,甜點是 奶茶 我的甜點是 紅豆餅 ,甜點是 綠茶 ### 5-4-4 任意引述串列 - Python 支援**任意引數串列(arbitrary argument list)** 的功能,也就是<span class='red'>函式可以接受不限定個數的參數</span>。 - 只要定義函式時將參數加上星號( * ),表示接受不限定個數的參數。 ```python= def add(*numbers): total = 0 print("numbers = ", numbers, type(numbers)) for i in numbers: total = total + i return total print(add(1)) print(add(1, 2)) print(add(1, 2, 3)) print(add(1, 2, 3, 4)) print(add(1, 2, 3, 4, 5)) ``` >Output: >> numbers = (1,) <class 'tuple'> 1 numbers = (1, 2) <class 'tuple'> 3 numbers = (1, 2, 3) <class 'tuple'> 6 numbers = (1, 2, 3, 4) <class 'tuple'> 10 numbers = (1, 2, 3, 4, 5) <class 'tuple'> 15 ## 5-5 函式的回傳值 - 原則上,<span class='red'>在def區塊內的敘述執行完畢之前,程式的控制權都不會離開這個函式</span>。 - 不過有時我們可能需要提早離開函式,返回呼叫函式的地方,此時可以使用 return 敘述。 - 或者我們可能需要從函式回傳某個值或某些值,此時可以使用 return 敘述,後面再加上傳回值。 ## 5-6 全域變數與區域變數 - 變數的有效範圍(scope),指的是程式的哪些敘述能夠存取變數的值。 - 大部分 Python 的變數都只有一種有效範圍,也就是程式的所有敘述都能存取變數的值,這種稱為全域變數(global variable)。 - 但在函式內定義的變數則稱為區域變數(local variable),只有函式內的敘述能夠存取區域變數的值。 ```python= def func1(): x = 1 print("x = ", x) func1() print("x = ", x) ``` >Output: >> x = 1 ERROR! Traceback (most recent call last): File "< main.py >", line 7, in < module > NameError: name 'x' is not defined ## 5-7 遞迴函式 - <span class='red'>遞迴函式(recursie function)是可以呼叫自己本身的函式。</span> - 若函式 f1( ) 呼叫函式 f2( ),而函式 f2( ) 又在某種情況下呼叫函式f1( ),那麼函式 f1( ) 也可以算是一個遞迴函式。 - <span class='red'>遞迴函式通常可以被 for 或 while 迴圈取代</span>。 - 由於遞迴函式的邏輯性、可讀性及彈性均比迴圈來得好,所以在很多時候,尤其是要撰寫遞迴演算法,還是會選擇遞迴函式。 ```python= # 使用迴圈 def factorial(n): result = 1 for i in range(1, n+1): result = result * i print(n,"! = ", result) factorial(eval(input("Please input a number: "))) ``` ```python= # 使用遞迴 def F(n): if n == 0: return 1 elif n > 0: # n! = n * (n-1)! return n * F(n-1) elif n < 0: return -1 result = F(eval(input("Please input a number: "))) print("Result = ", result) ``` :::success 1. 很明顯的遞迴函式比 for 迴圏來得有彈性。 2. 遞迴函式的語法並不難,重點在於如何設計遞迴演算法,而這需要演算法的基礎。 ::: ## 5-8 lambda運算式 - Python提供一個<span class='red'>lambda關鍵字,可以用來建立小的匿名函式</span>。 - 所謂的匿名函式(anonymous function),指的是沒有名稱的函式。 ```python! lambda arg1, arg2, ...: expression ``` - lambada運算式會產生一個函式物件,arg1, arg2, ...就相當於函式定義的參數。 - expression 就相當於函式定義的主體 我們可以在 expression 中使用 arg1, arg2, ...這些參數。 ```python= add = lambda a,b: a+b print(add(1, 2)) print(add(400, -20)) ``` >Output: >>3 380