函式
函式是把一段程式包起來、取個名字,之後要用就呼叫它,不必一直重寫。這一章看怎麼定義函式、傳資料進去、把結果拿回來。
用 def 定義函式:def 後面接名字和一對括號,底下縮排的就是函式的內容。定義好之後,寫「名字加括號」就能呼叫它:
def greet(name): print("你好," + name)
greet("小明")括號裡的 name 是參數,呼叫時給的 "小明" 會被塞進去。按「下一步」時注意,呼叫 greet 那一刻,右邊的呼叫堆疊會多出一層,跳進函式裡執行。
回傳值:return
Section titled “回傳值:return”前面的 greet 只是印東西。更常見的是讓函式算出一個結果交回來,用的是 return:
def add(a, b): return a + b
result = add(3, 5)print(result)add(3, 5) 執行到 return a + b,就把 8 交回呼叫的地方,存進 result。函式一旦 return,就結束、跳回原來的位置。
return 也可以一次回傳好幾個值,用逗號隔開;接收時同樣用逗號一次拆開:
def divide(a, b): return a // b, a % b
quotient, remainder = divide(17, 5)print("商是", quotient)print("餘數是", remainder)return a // b, a % b 一口氣把商和餘數都交回來,quotient, remainder = divide(17, 5) 再一次接住兩個。
參數的預設值
Section titled “參數的預設值”參數可以給預設值。呼叫時沒提供,就用預設的;有提供,就用你給的:
def greet(name, greeting="你好"): print(greeting + "," + name)
greet("小明")greet("小美", "早安")greet("小明") 沒給 greeting,用預設的「你好」;greet("小美", "早安") 則把預設值蓋掉。
定義時有一條規則:有預設值的參數要放在沒有預設值的後面。寫成 def greet(greeting="你好", name) 是不行的,Python 會直接報 SyntaxError。原因是呼叫時的值是從左邊開始填的,greet("小明") 會填到 greeting,反而讓 name 沒有值,這個預設值就永遠派不上用場。
位置引數與關鍵字引數
Section titled “位置引數與關鍵字引數”先把兩個詞分清楚:定義函式時寫在括號裡的名字叫參數(parameter),像 describe(name, age) 裡的 name 和 age;呼叫時實際交進去的值叫引數(argument),像 describe("小明", 18) 裡的 "小明" 和 18。呼叫要做的事,就是把每個引數對到一個參數。Python 有兩種對法:靠位置,或靠名字。
def describe(name, age): print(name, "今年", age, "歲")
describe("小明", 18)describe(age=20, name="小美")describe("小明", 18) 沒有寫名字,Python 就照順序對:第一個引數給第一個參數 name,第二個給 age。這種叫位置引數。describe(age=20, name="小美") 用 參數名=值 的寫法直接指名,這種叫關鍵字引數,對的是名字,所以順序可以隨便排。步進時看右邊的呼叫堆疊,兩次呼叫裡 name 和 age 拿到的值都是對的。
位置引數:照順序,數量要剛好
Section titled “位置引數:照順序,數量要剛好”位置引數的規則只有一條:第幾個引數,就給第幾個參數。Python 只認順序,不會管值合不合理,所以順序放反了它不會發現,只會給你一個怪怪的結果:
def describe(name, age): print(name, "今年", age, "歲")
describe("小明", 18)describe(18, "小明")第二次呼叫把 18 給了 name、"小明" 給了 age,印出「18 今年 小明 歲」。程式沒有報錯,這種錯只能靠自己看出來。
數量 Python 倒是會檢查:函式定義了幾個參數,位置引數就要給剛好幾個(有預設值的可以省略,後面會講)。少給,Python 會告訴你哪個參數沒拿到值:
# 最後一行故意寫錯,看 Python 會報什麼錯。# type: ignore[ty:missing-argument]
def describe(name, age): print(name, "今年", age, "歲")
describe("小明", 18)describe("小美")describe("小美") 只給了一個,age 沒有值,程式停下來、報出 TypeError: describe() missing 1 required positional argument: 'age'。多給也一樣不行:
# 最後一行故意寫錯,看 Python 會報什麼錯。# type: ignore[ty:too-many-positional-arguments]
def describe(name, age): print(name, "今年", age, "歲")
describe("小明", 18)describe("小美", 20, 165)describe 只有兩個參數,卻塞了三個值,第三個 165 沒地方放,報的是 TypeError: describe() takes 2 positional arguments but 3 were given。訊息裡直接寫了「定義幾個、給了幾個」,對照函式定義就能找到問題。
關鍵字引數:名字要對得上
Section titled “關鍵字引數:名字要對得上”關鍵字引數的規則也只有一條:等號左邊要和定義裡的參數名一模一樣。名字對上了,順序就無所謂;名字對不上,就算只差一個字母,Python 也不會幫你猜:
# 最後一行故意寫錯,看 Python 會報什麼錯。# type: ignore[ty:unknown-argument, ty:missing-argument]
def describe(name, age): print(name, "今年", age, "歲")
describe(name="小明", age=18)describe(name="小美", year=20)describe 的參數叫 age,不叫 year,所以 year=20 對不到任何參數,報 TypeError: describe() got an unexpected keyword argument 'year'。看到「unexpected keyword argument」,就回去對照函式定義,通常是名字打錯,或記錯了參數叫什麼。
兩種混用時的規則
Section titled “兩種混用時的規則”一次呼叫裡可以同時用兩種,最常見的寫法是前面幾個靠位置、後面幾個指名:
def describe(name, age): print(name, "今年", age, "歲")
describe("小明", age=18)describe("小美", 20)describe(name="小華", age=22)describe("小明", age=18):"小明" 沒寫名字,照位置給第一個參數 name;age=18 指名給 age。混用時要守兩條規則。
第一條:位置引數要全部放在關鍵字引數前面。位置引數靠「第幾個」對應,一旦前面出現了指名的引數,後面再出現沒名字的值,Python 就數不出它是第幾個了。這條規則 Python 在執行前就會檢查,所以違反了是 SyntaxError,程式一步都不會跑:
def describe(name, age): print(name, "今年", age, "歲")
describe(name="小明", 18)describe(name="小明", 18) 先指名再給位置,報 SyntaxError: positional argument follows keyword argument。改成 describe("小明", age=18),或乾脆全部指名 describe(name="小明", age=18),就都可以。
第二條:每個參數只能收到一個值。前面已經用位置給過的參數,後面不能再用名字給一次:
# 最後一行故意寫錯,看 Python 會報什麼錯。# type: ignore[ty:parameter-already-assigned, ty:missing-argument]
def describe(name, age): print(name, "今年", age, "歲")
describe("小明", age=18)describe("小美", name="小華")describe("小美", name="小華") 裡,"小美" 照位置已經給了 name,name="小華" 又要再給一次,Python 不知道該用哪個,報 TypeError: describe() got multiple values for argument 'name'。
什麼時候該用關鍵字引數
Section titled “什麼時候該用關鍵字引數”關鍵字引數最常用在有預設值的函式上。參數多、又大多有預設值時,你往往只想改其中一個:
def order(drink, size="中杯", ice="正常冰"): print(size, ice, drink)
order("紅茶")order("紅茶", "大杯", "去冰")order("紅茶", ice="去冰")order("紅茶", ice="去冰") 只改冰量,size 維持預設。如果只能用位置引數,就得寫成 order("紅茶", "中杯", "去冰"),把不想改的 "中杯" 也重打一次,才能把 "去冰" 排到第三個位置。另一個好處是可讀性:describe("小明", 18) 要回頭看定義才知道 18 是什麼,describe(name="小明", age=18) 一眼就懂。
- 位置引數:照順序對應,第幾個給第幾個參數;數量要和參數一樣多,有預設值的可以省略。
- 關鍵字引數:寫
參數名=值,名字要和定義一字不差;順序隨意。 - 混用:位置引數全部在前、關鍵字引數全部在後,違反是
SyntaxError。 - 每個參數只能拿到一個值:少給、多給、重複給都是
TypeError,訊息會告訴你是哪個參數出問題。
提早 return
Section titled “提早 return”return 不一定要放在最後。一遇到就可以回傳、結束函式。這常拿來先處理特殊情況,把主要的邏輯留到後面,不用層層巢狀的 if:
def check_age(age): if age < 0: return "年齡不能是負的"
if age < 18: return "未成年"
return "成年"
print(check_age(-5))print(check_age(15))print(check_age(30))年齡是負的就直接回傳、不再往下;否則再看是不是未成年;都不是,才走到最後的「成年」。
接收任意多個引數:*args 與 **kwargs
Section titled “接收任意多個引數:*args 與 **kwargs”有時你不知道會傳幾個引數進來。參數前面加一個 *,就會把所有位置引數收集成一個元組(tuple)。元組是一排不能改的資料,「元組、集合、字典」那章會詳細講,這裡只用到「能用 for 走過它」這件事:
def total(*numbers): result = 0 for n in numbers: result = result + n
return result
print(total(1, 2, 3))print(total(10, 20, 30, 40))total(1, 2, 3) 和 total(10, 20, 30, 40) 傳的個數不同,函式裡的 numbers 都能一次收下、用迴圈加總。
加兩個 *(**)則是把關鍵字引數收集成一個字典(dictionary),以參數名當鍵、對應的引數當值:
def show_info(**info): for key in info: print(key, ":", info[key])
show_info(name="小明", age=18)info[key] 是用鍵取出對應的值,字典那章會講。呼叫時的 name 和 age 都沒有寫在函式定義裡,info 全收下了。參數名 args、kwargs 只是慣例,換成別的名字也行,重點是前面的 * 和 **。
函式可以呼叫自己,這叫遞迴。關鍵是要有個停下來的條件,否則會無止盡地叫下去。下面用遞迴算階乘(5 的階乘是 5×4×3×2×1):
def factorial(n): if n == 0: return 1
return n * factorial(n - 1)
print(factorial(5))一直按「下一步」,看右邊的呼叫堆疊怎麼一層一層疊上去:factorial(5) 要先算 factorial(4),factorial(4) 又要 factorial(3)……一路到 factorial(0) 回傳 1,才逐層乘回來,最後得到 120。這個「堆疊疊起來又收回去」的過程,用步進看最清楚。
v0.9.1 (c924b5b)