函式 function
當程式越來越複雜的時候,就必須將一些重複或有特別定義的程式,拆分成容易管理的小程式,這些小程式就稱為「函式」,函式是一種有名稱且獨立的程式片段,可以接收任何型態的參數,處理完成後也可以輸出任何型態的結果。
快速導覽:定義函式、函式參數、參數預設值、關鍵字引數、函式回傳值、函式內的函式、函式內的變數、*args、**kwargs 運算子、使用 pass
本篇使用的 Python 版本為 3.7.12,所有範例可使用 Google Colab 實作,不用安裝任何軟體 ( 參考:使用 Google Colab )
定義函式
在 Python 裡,使用「def
」定義一個函式,函式的命名規則和變數相同 ( 只能以字母或底線開頭,內容只能使用字母、數字或底線 ),下方程式碼是一個名為 hello 函式的基本架構:
def 後方通常會放上函式名稱 ( 名稱不能和變數名稱重複 )、輸入參數的小括號,後方再加上一個冒號,函式的程式採用「縮排」的方式表現 ( 參考:縮排 )。
def hello():
print('hello')
hello() # 執行函式,印出 hello
注意,一定要「先定義函式,再執行函式」,不然執行時會發生錯誤,下方的程式將執行函式放在定義函式之前,執行時就會發生錯誤。
hello()
def hello():
print('hello') # 發生錯誤 name 'hello' is not defined
函式參數
函式可以加入「參數」,執行函式時給予這些參數指定的數值 ( 引數 ),就能讓函式根據不同參數的內容,計算出不同的結果。
下方的 hello 函式有一個 msg 參數,執行函式時如果給予的內容不同,就會呈現不同的結果。
def hello(msg):
print(msg)
hello('hello') # hello
hello('good morning') # good morning
一個函式可以放入「多個」參數,下方的 hello 函式具有 x 和 y 兩個參數,根據不同的參數數值進行計算,最後呈現不同數值的加總結果。
注意,執行函式時,會按照「順序」處理多個參數,例如函式參數順序如果是 (x,y),執行時填入 (1,2),x 就會是 1,y 就會是 2。
def hello(x,y):
z = x + y
print(z)
hello(1,2) # 3
hello(5,6) # 11
參數預設值
函式的參數可以「指定預設值」,如果執行函式時沒有提供參數數值,參數就會自動帶入預設值執行,下方的程式設定參數 y 的預設值為 10,如果執行時沒有提供參數 y 數值,y 就會使用 10 帶入計算。
def hello(x,y=10):
z = x + y
print(z)
hello(1,2) # 3
hello(5) # 15
關鍵字引數
函式除了透過「順序」( 位置 ) 指定參數外,也可以使用「關鍵字引數」( 指定的名稱 ) 來設定特定的參數內容 ( 引數表示提供給參數的數值 )。
下方的程式裡,hello 函式有 name 和 age 兩個參數,如果執行函式時提供的數值順序不同,產生的結果就會不同,如果額外使用關鍵字引數,就算提供的內容順序不同,仍然會是正確的結果。
def hello(name, age):
msg = f'{name} is {age} years old'
print(msg)
hello('oxxo',18) # oxxo is 18 years old
hello(18,'oxxo') # 18 is oxxo years old ( 因為 18 和 oxxo 對調,所以結果就會對調 )
hello(age=18,name='oxxo') # oxxo is 18 years old ( 使用關鍵字引數,結果就會是正確的 )
此外,在定義函式時也可以賦予關鍵字引數內容,執行後就算沒有提供該引數的數值,仍然會套用預設值,以下方的程式為例,如果不指定 start 和 end,就會預設使用 0 和 3。
def test(a, start=0, end=3):
for i in a[start:end]:
print(i)
b = [1,2,3,4,5]
test(b, start=2, end=len(b)) # 3 4 5
test(b) # 1 2 3
函式回傳值
函式除了可以傳入參數,也可以使用「return」回傳程式運算後的結果,回傳的結果不限型態,可以是數字、字串、串列、tuple...等,下方的程式碼,執行函式 a 之後,函式會計算並回傳 x + y x 2 的結果,最後將結果賦值給變數 b 和 c。
def a(x, y):
result = x + y*2
return result
b = a(1,2)
c = a(2,3)
print(b) # 5
print(c) # 8
當函式執行的過程中遇到 return 時,就會「中止函式」並將結果回傳,以下方的程式為例,當 x=1 ( 每次 result 增加 1 ) 的時候,會觸發 result==5 的邏輯判斷,就會中止函式 ( 函式內的 while 迴圈也就跟著停止 ),並回傳 5 的結果,當 x=2 ( 每次 result 增加 2 ) 的時候,不會處發 result==5 的邏輯判斷,就會執行完 while 迴圈,最後回傳 10 的結果。
def a(x):
result = 0
while result < 10:
result = result + x
if result==5:
return result
return result
b = a(1)
c = a(2)
print(b) # 5
print(c) # 10
函式也可以回傳多個結果,如果回傳多個結果,可以賦值給「同樣數量」的變數 ( 不同數量會發生錯誤 )。
def test(x, y, z):
return x+1, y+1, z+1
a, b, c = test(1, 2, 3) # 賦值給「同樣數量」的變數
print(a) # 2
print(b) # 3
print(c) # 4
函式回傳的多個結果也可以只賦值給一個變數,這時就會將多個結果變成一個 tuple。
def test(x, y, z):
return x+1, y+1, z+1
a = test(1, 2, 3)
print(a) # (2, 3, 4)
函式內的函式
在一個函式裡也可以放入另外的函式,形成函式內的函式 ( 某些情況下會成為「閉包」 ),但函式內的函式只能在函式裡使用,下方的程式碼,在 hello 函式裡,建立了 h1 和 h2 的內部函式,根據不同的參數執行不同的函式,如果在外部執行內部函式,就會發生錯誤。
def hello(n, msg):
def h1(): # 內部函式
return msg
def h2(): # 內部函式
return msg*2
if n == 1:
print(h1())
if n == 2:
print(h2())
hello(1, 'ok') # ok
hello(2, 'ok') # okok
print(h2()) # 發生錯誤 name 'h2' is not defined
函式內的變數
如果放在函式裡的變數,沒有經過 global 的宣告,就會成為「區域變數」( 更多參考:全域變數、區域變數 )。
a = 123 # 全域變數 a
b = 123 # 全域變數 b
def hello(msg):
a = msg # 區域變數 a,更動區域變數不影響全域變數
print(a)
global b # 宣告變數 b 是使用全域變數 b,更動變等同更動全域變數
b = msg
hello(456) # 456
print(a) # 123
print(b) # 456 被更改為 456
如果函式裡又有其他函式,需要使用區域變數,可以將變數宣告為 nonlocal 的自由變數,就能自由地在函式裡使用,下方的程式碼有宣告 a 為自由變數,所以執行後會正常運作,但因為 b 沒有宣告為自由變數,所以使用時就會發生錯誤 ( 更多參考:自由變數 nonlocal )。
def hello(msg):
a = 123
b = 123
def h1():
nonlocal a # 宣告 a 為自由變數
a = a + msg
print(a)
def h2():
b = b + msg
print(b)
h1() # 579
h2() # 發生錯誤 local variable 'b' referenced before assignment
hello(456)
*args、**kwargs 運算子
如果把函式的參數設定帶有 args ( 一個星號 *
) 運算子的參數,則傳入的所有參數,都會被組合成 tuple 的型態,下方的函式使用了「*args
」的參數,執行函式時不論給予多少引數,最後都會組合成 tuple。
args 和 kwargs 的英文名稱只是「變數名稱」,可以自由更換,重點在於前方的一個星號與兩個星號。
def test(*args):
print(args)
test(1,2,3,'a','b','c')
# (1,2,3,'a','b','c')
如果把函式的參數設定帶有 kwargs ( 兩個星號 **
) 運算子的參數,則傳入的所有「帶有關鍵字引數」的參數,都會被組合成字典的型態,下方的函式使用了「**kwargs
」的參數,執行函式時不論給予多少引數,最後都會組合成字典。
def test(**kwargs):
print(kwargs)
test(name='oxxo',age=18,like='book')
# {'name': 'oxxo', 'age': 18, 'like': 'book'}
如果 *args
、**kwargs
同時出現,則會根據輸入的內容,分別套用 *args
或 *kwargs
,下方的 a 函式在執行時,傳入不同的參數,最後呈現的結果就會按照參數型態的不同而有所區隔。
def a(*args, **kwargs):
print(args)
print(kwargs)
a([123, 456], x=1, y=2, z=3)
# ([123, 456],)
# {'x': 1, 'y': 2, 'z': 3}
同理,如果將一個星號套用在 print() 裡 ( print() 算是一個內建函式 ),就會將可迭代的物件打散後印出。
a = [1,2,3,4,5]
b = (1,2,3,4,5)
c = {'x':1,'y':2,'z':3}
d = {'x','y','z'}
print(*a) # 1 2 3 4 5
print(*b) # 1 2 3 4 5
print(*c) # x y z
print(*d) # x y z
使用 pass
如果想定義一個什麼事都不做的空函式,可以使用 pass 語句:
def test():
pass
pass 除了可以應用在函式,也可以使用在判斷式裡,作為一個佔位符使用 ( 不會執行任何事情,但必須出現的程式碼 )。
a = int(input('>'))
if a>10:
pass # 如果輸入的數字大於 10,不做任何事情
else:
print(a)
意見回饋
如果有任何建議或問題,可傳送「意見表單」給我,謝謝~