Python 中的函式 (Function) 觀念 (Part 1)

1 前言
在前幾篇的 Python 教學文章中,我們已經用過「函式」(Function) 這個詞,也實際呼叫過幾個 Python 的內建函式 (Built-in Function),像是 print()、len() 與 input()。不過到目前為止,我們都只是「使用別人寫好的函式」。
這篇文章要把這件事翻到另一面:函式到底是什麼、為什麼要有函式,以及怎麼定義自己的函式。內容會從最基本的函式寫法開始,接著談帶有參數的函式、帶有回傳值的函式,最後補上一個常常讓初學者卡住的觀念 —— NoneType 與 None。
說白了,函式就是把一段「會重複用到、或是有明確任務」的程式碼包成一包,給它一個名字,之後想用的時候喊一聲名字就好,不必把同樣的程式碼再抄一次。
2 Python 中的函式 (Function)
先看以下這段程式碼:
def first_function():
print('Hello World')
first_function()這段程式碼做了兩件事:定義一個名為 first_function 的函式,然後呼叫它。我們把它拆開來看組成元素。
第 1 行的 def 是關鍵字,表示我們正在「定義」一個函式。跟在 def 之後的 first_function 就是這個函式的「名稱」,之後要呼叫它就靠這個名字。名稱後面還要再加上一組「括號」,括號的用途等一下會解釋。
寫好 def、函式名稱與括號之後,還得說明函式內部要執行哪些程式碼。這裡的語法和 for loop、while loop 一樣:行尾加上一個「冒號」,函式內部的程式碼則必須「縮排」。縮排的這幾行就是函式的內容,只有在函式被呼叫時才會執行。以上面的例子來說,函式內部就只有 print('Hello World') 這一行。
程式碼區塊的最後一行 first_function() 就是在「呼叫」這個函式。呼叫的方式很簡單,寫出「函式名稱」加上「括號」即可。
那括號到底是做什麼的?括號裡可以提供一些參數給這個函式。例如我們用內建的 print() 函式時,就是把想顯示的字串放進括號中:
print('Hello World')至於執行的順序,可以這樣理解:當電腦執行到「呼叫函式」那一行時,會先跳到該函式內部的第一行開始執行;等函式裡的程式碼全部執行完,再跳回剛剛呼叫的地方,繼續往下執行後面的程式碼。
舉例來說:
def first_function():
print('Hello World')
first_function() #1
print('執行完 1 次') #2
first_function() #3
print('執行完 2 次') #4第一次呼叫 first_function (#1) 之後,電腦會跳進 first_function 執行 print('Hello World'),執行完函式內所有程式碼後,再回到 #1 的位置,繼續執行 #2、#3、#4。所以最終的輸出為:
Hello World
執行完 1 次
Hello World
執行完 2 次注意 Hello World 出現了兩次,但我們只寫了一次 print('Hello World')。這就是函式最直接的好處:同一段邏輯只寫一次,要用幾次就呼叫幾次。
3 Python 中帶有參數的函式
一路用到現在,你應該已經很習慣透過 print() 顯示想要的字串。我們放在括號中的那個字串稱為 Argument,它會在呼叫函式的當下被傳進函式裡面。
自己定義的函式當然也能接收 Argument:
def second_function(name):
print(f'Hello, {name}')
second_function('Johnny')在函式名稱 second_function 後方的括號中,我們放入一個變數 name,這個變數稱為 Parameter,用來規定這個函式可以接受什麼東西傳進來。因此呼叫時我傳入字串 'Johnny',顯示結果為:
Hello, Johnny看到這裡,也許你已經被 Parameter 與 Argument 搞得暈頭轉向。其實不必太糾結這兩個專有名詞,只要記得它們分別出現在哪個階段:
- Parameter:在「定義函式」階段,規定這個函式能夠接受哪些東西。例如上方程式碼中的
name。 - Argument:在「呼叫函式」階段,實際上傳進函式的東西。例如上方程式碼中的
'Johnny'。
有一件事要特別注意:函式所接受的 Parameter 在函式內部可以當作一般變數使用,但函式執行完畢之後,這個變數就會被消滅,所以在函式外面是拿不到它的。
舉例來說,我們在 second_function('Johnny') 的下方加上一行 print(name):
def second_function(name):
print(f'Hello, {name}')
second_function('Johnny')
print(name)執行的結果為:
NameError: name 'name' is not defined原因就在於 name 這個變數在 second_function 執行完畢之後就被消滅了,因此無法在函式的外面使用它。
4 Python 中帶有回傳值的函式
除了 print() 之外,我們也很常用 len() 函式取得字串的長度:
name = 'Johnny'
length = len(name)
print(length)這段程式碼會顯示 name 這個字串的長度。這裡的關鍵是:len() 被呼叫之後會「回傳」一個整數,我們才有辦法把它存進 length 變數中。這和 print() 不一樣 —— print() 只是把東西顯示在畫面上,並沒有交還什麼可以拿去用的結果。
如果希望自己寫的函式在執行完之後,也能回傳數值到原來呼叫的地方,就必須使用 return 關鍵字:
def third_function(name):
return f'Hello, {name}'
output = third_function('Johnny')
print(output)在 third_function 中,我們透過 return 回傳一個字串。因此 third_function('Johnny') 執行完的結果會是 Hello, Johnny,並存進 output 變數中。
另外要注意的是,當函式執行到 return 時,就代表這個函式結束了,return 之後的程式碼不會被執行:
def fourth_function(name):
return f'Hello, {name}'
print('under return keyword')
output = fourth_function('Johnny')
print(output)這段程式碼的執行結果會與前一段完全相同。因為在 fourth_function 中,print('under return keyword') 位在 return 之後,所以永遠不會被執行到。
5 Python 中的 NoneType 與 None
在先前介紹 Python 變數與資料類型的文章中,我們提過 Python 的基本資料型態,例如 Integer、String 與 Floating-Point Number。
今天要再介紹一種新的 Data Type,稱為 NoneType。NoneType 很特別,它只有唯一一個數值,就是 None。一個 Type 裡頭只包含 1 個或 2 個數值其實並不奇怪,我們之前學過的 Boolean Type 裡頭就只有 True 與 False 兩種數值。
那為什麼要多學 NoneType 這種看起來有點神奇的型態?原因和函式的回傳值有關。
在 Python 中,每一個函式都會有回傳值,也就是每次呼叫函式之後都一定會得到一個數值。呼叫 third_function()、fourth_function() 與 len() 都可以得到一個數值,這很直覺。但問題是,如果函式要有回傳值就必須寫 return ...,而我們並不是每次都會寫這一行 —— 像本文最開頭的 first_function() 就沒有。
Python 的處理方式是:只要我們定義的函式裡沒有 return ...,Python 就會自動幫我們補上一個 return None。回頭看看 first_function():
def first_function():
print('Hello World')
output = first_function()
if(output == None):
print(f"output is None")我們把 first_function() 的執行結果存到 output 變數中,再判斷 output 是不是等於 None。最後的輸出為:
Hello World
output is None由此可以確認:即使 first_function() 中沒有回傳任何數值,也就是沒有寫 return ...,Python 仍然會自動補上 return None,使得函式最終的回傳值是 None。這也是為什麼把 print() 的結果存進變數再印出來,看到的會是 None 而不是剛剛顯示的那串字。
6 結論
這篇文章從最基本的函式定義開始,一路走到帶有參數的函式、帶有回傳值的函式,並且說明了 Python 中 NoneType 與 None 的觀念。掌握這幾點之後,你已經可以把重複的程式碼包成函式,讓程式碼變得更好讀、也更好維護。
不過還有一個問題還沒回答:函式裡面的變數到底「活」在哪個範圍?下一篇文章 (Part 2) 會介紹函式更深入的觀念,也就是函式的範疇 (Function Scope)。




