###### [Python 教學/](https://hackmd.io/7-LP9CyOThOdkEbq44FLvw) # 函式(Function) > [name=VJ][time= 109,11,17] > [name=Hui][time=110,11,18] --- 函式是程式碼中的一個區塊,只有當它被呼叫時才會運行。 你可以將資料,傳遞到一个函式中。 **函式可以返回資料作為結果**。 --- 在思考上,我們可以把函式(Function)想成是**一段程式碼**的包裝,提高編輯程式語言的效率 又或者是我們可以想成是**數值的轉換**關係,藉由傳入的數值來快速有效的得到我們實際需要的數值 --- ## 建立函式 Python 中我們使用**關鍵字**`def`來定義函式: 如同我們之前做(if、elif)的一樣,要在後面加冒號然後縮排 ```python= def my_function(): print("Hello from a function")#實際上"print()"就是一個函式 ``` ## 呼叫函式 ```python= def my_function(): print("Hello from a function") my_function() ###呼叫 ``` --- ## 引數、參數(Arguments, Parameters) 資料可以作為引數傳遞到一个函式中作為參數使用。 ex: ```python= def i_am_a_function(ppp): #建立函式 print(ppp) aaa=999 i_am_a_function(aaa) #呼叫函式 ``` --- ppp 參數 aaa 引數 --- #### 建立函數時 我們可以在函數建立時預先設定好任意數量的**參數** 這些數可能是我們在執行函式時可能需要用到的,一般來說會在呼叫時來決定具體的數值zz #### 呼叫函數時 在呼叫時,我們可能需要傳遞零至數個**引數**給函式,希望函數能依據這些數值來達成我們期望的結果 --- ### 舉例 下面的例子中有一個含一個參數(`fname`)的函式。當函式被呼叫時,我們傳遞了一個字的名字,這個名字在函式內部用來輸出全名。 ```python= def my_function(fname): print('小' + fname) my_function('明') my_function('夫') my_function('萱') ``` --- ### 多個參數 正常來說,函式必須用正確數量的引數來呼叫。意思是說,如果你的函式有2個參數,你必須用2個引數來呼叫函式,不能多,也不能少,不然就會報錯。 --- ::: spoiler 練習1 ```python= def my_function(fname, lname): print(fname +' ' + lname) my_function('VJ','Tan') ``` ::: 執行下面程式碼會報錯,請修改第4行讓它能運行。 ```python= def my_function(fname, lname): print(fname + ' ' + lname) my_function('VJ') ##改這行 ``` --- ## 回傳值 要讓一個函式回傳一個值(或資料),請使用`return`。 ```python= def is_bigger_then_0(n): if n>0: return True else : return False ``` >是否大於零 --- ```python= def my_function(x): mylist=[] for i in range(x): mylist.append(i) return mylist print(my_function(3)) print(my_function(5)) print(my_function(9)) ``` >回傳1~n 的list --- ::: spoiler 練習2 ```python= def isPrime(n): if(n==2): return True for i in range(2,int(n**0.5)+1): if(n%i==0): return False return True print(isPrime(1117)) ``` ::: 寫一個含一個引數的函式(如下),判斷`n`是不是質數(回傳`True`或`False`)。 ```python= def isPrime(n): ## TO-DO print(isPrime(1117)) ``` --- ::: spoiler 練習3 ver.1.0 ```python= def func(n=int(input('輸入n:'))): for i in range(n+1): for j in range(i+1): print('*',end='') print() func() ``` ver.2.0 ```python= def func(n): #此function 為 印出一行 n 個星星 for i in range(n): print('*',end='') print() times=int(input('輸入n:')) for i in range(times): #執行function n次 func(i) ``` ::: 輸入: 整數n 輸出: nxn的直角三角形 ps使用function --- ::: spoiler 練習4 ```python= def mean(a,b,c): return (a+b+c)/3 print(mean(3,2,7)) ``` ```python= mean = lambda a,b,c : (a+b+c)/3 print(mean(3,2,7)) ``` ::: 輸入: `a,b,c` 輸出: 平均值 (也用可以用下面的`lambda`試試看) --- ## 全域,區域變數 Python中,區分全域及區域變數,需在函式區塊中第1行用`global`宣告它。 ```python= x=27 def func(): global x x=3 #func()中的區域變數x print(x) func() #輸出3 print(x) #輸出3 ``` --- 無論任何程式語言,會糾結全域或區域變數的情況通常就只是變數名稱一樣。~~(改不一樣就沒這回事啦)~~ ```python= x=27 def func(): x=3 #func()中的區域變數x print(x) func() #輸出3 print(x) #輸出27 ``` --- ## 關鍵字參數 Python 可以用 key = value 語法傳遞引數。 這樣一來,引數的順序就不重要了。 ```python= def func(child1, child2, child3): print("第三個孩子: " + child3) func(child3 = "大雄", child2 = "小夫", child1 = "胖虎") ``` --- ## 參數預設值 下面的例子展示如何設定參數的預設值。 如果我們在沒有引數的情況下呼叫函式,它就會使用參數的預設值。 ```python= def my_function(country = "台灣"): print('我來自' + country) my_function("韓國") my_function("日本") my_function() my_function("澳門") ``` --- ## 參數預設類型 下面的例子展示如何設定參數的預設類型。 我們必須根據函式預設的類型傳遞引數,不然會報錯。 ```python= def my_function(country:str): print("I am from " + country) my_function("Sweden") my_function("India") my_function("Brazil") my_function(123) ##錯誤 ``` --- ::: spoiler 練習5 ```python= def bmiRec(h:int,w:int,date:int,name:str='無名氏'): bmi = w/(h/100)**2 for i in bmiv: if(bmi<i): v = vList[bmiv.index(i)] break if(bmi>=35): v = vList[5] rec = {'身高':h,'體重':w,'BMI':bmi,'狀態':v} record.setdefault(name,dict())[date] = rec ``` ::: 完成下面用於紀錄 [BMI](https://zh.wikipedia.org/wiki/%E8%BA%AB%E9%AB%98%E9%AB%94%E9%87%8D%E6%8C%87%E6%95%B8) 、身高、體重等相關資訊的函式`bmiRec()` ```python= vList=['過輕','正常','過重','輕度肥胖','中度肥胖','重度肥胖'] bmiv=[18.5,24,27,30,35] record = dict() # 或{} def bmiRec(h:int,w:int,date:int,name:str='無名氏'): ### 填充 ### rec = {"身高":h,"體重":w,"BMI":bmi,"狀態":v} ### 填充 ### bmiRec(name='VJ',h=150,w=45,date=20090702) bmiRec(name='VJ',h=165,w=50,date=20201117) for name,rec in record.items(): print(name,':') for date,reco in rec.items(): print(' ',date,':') for term,content in reco.items(): print(' ',term,':',content) ``` --- --- ## 遞迴 遞迴是一個常見的數學和程式概念。它意味著一個函式呼叫自己。 在使用遞歸時要非常小心,因為它很容易陷入一個永遠不會終止狀況,或者使用過量內存或處理器資源。但如果寫得正確,遞歸可以是一種非常高效和數學上優雅的編寫方法。 --- 下面函式展示了[費氏數列](https://zh.wikipedia.org/wiki/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0%E5%88%97)通項公式的運用 ```python= def fib(n=10): result = list() #result=[] for i in range(n): if i == 0: result.append(0) if i == 1: result.append(1) else: result.append(result[-1]+result[-2]) return result[-1] print(fib()) ``` --- ![](https://i.imgur.com/Scpr68a.png) 遞迴ver $f(n)=F_n$ ```python= def fib(n): if n==0: return 0 if n==1: return 1 else: return fib(n-1)+fib(n-2) ``` --- ::: spoiler 練習6 lambda ver ```python= GCD = lambda a, b: (GCD(b, a % b) if a % b else b) print(GCD(int(input('輸入a:')),int(input('輸入b:')))) ``` 一般ver ```python= def GCD(a, b): if (a % b): return GCD(b, a % b) else: return b a = int(input('輸入a:')) b = int(input('輸入b:')) print(GCD(a,b)) ``` ::: 寫出一個含兩個引數的函式求它們的[最大公因數](https://zh.wikipedia.org/wiki/%E6%9C%80%E5%A4%A7%E5%85%AC%E5%9B%A0%E6%95%B8) hint 輾轉相除法 ![](https://i.imgur.com/iWd3YCQ.png) --- ::: spoiler 練習7 ```python= def hanoi(n, a, b, c): if n == 1: print(a, '-->', c) else: hanoi(n - 1, a, c, b) hanoi( 1, a, b, c) hanoi(n - 1, b, a, c) hanoi(5, 'A', 'B', 'C') ``` ::: [河內塔](https://zh.wikipedia.org/wiki/%E6%B1%89%E8%AF%BA%E5%A1%94) \*經典題 輸入:n n代表 塔有n層 輸出:塔片的移動情形 >hint:A,B,C 代表塔柱的塔片 --- ex hanoi(3) A --> C A --> B C --> B A --> C B --> A B --> C A --> C --- ::: spoiler 練習8 ```python= def hanoi(n, a, b, c, number, ): global target, move if n == 1: move +=1 if move == target: print(number, '|', a, '-->', c) else: hanoi(n - 1, a, c, b,n-1) hanoi( 1, a, b, c,n) hanoi(n - 1, b, a, c,n-1) n=int(input()) target=int(input()) move=0 hanoi(n, 'A', 'B', 'C',n) ``` ::: 輸入: 兩正整數a,b,a為河內塔總層數 輸出: a塔在第b次的移動情況 ``` 輸入 8 9 輸出 1|B->C ``` ``` 輸入 6 4 輸出 3|A->B ``` <style>hr{display:none;}</style> # 稍微進階的用法 ## 任意參數`*args` 如果你不知道有多少個引數將被傳遞到你的函式中,在函式定義中的參數名前加一個`*`。 這樣函式就會收到一個引數的**元組**,並能相應地訪問這些項目。 ```python= def my_function(*kids): print("The youngest child is " + kids[2]) my_function("Emil", "Tobias", "Linus") ``` --- ## 任意關鍵字參數`**kwargs` 如果你不知道有多少個關鍵字(kw)參數將被傳遞到你的函式中,請在函式定義中的參數名前加上兩個星號。在函式定義中的參數名前加上`**`。 這樣函式就會收到一個參數**字典**,並能據此訪問這些項目。 *注: 變數命名限定(不能打中文、不能用數字開頭等)* ```python= def my_function(**kid): print("His last name is " + kid["lname"]) my_function(fname = "Tobias", lname = "Refsnes") ``` 另外,在宣告時 **任意參數**一定要放在**任意關鍵字參數** 前面 ```python= def my_function(arg0,arg1,*args,**kwargs): #to do... pass ``` --- ## `lambda`函式(匿名函式) 匿名函式,顧名思義,就是沒有名字的函式,只有函式的實體 `lambda`函式可以接受任意數量的引數,但只能有一個表達式用作回傳。 ```python= x = lambda a, b : a * b print(x(5, 6)) # print((lambda a, b : a * b)(5, 6)) ``` 等同於... ```python= def x(a,b): return a * b print(x(5, 6)) ``` 常見的使用時機 ```python= scores = [ ('Jane', 'B', 12), ('John', 'A', 15), ('Dave', 'B', 11)] # 依照第三個數字元素排序 key = lambda s: s[2] scores.sort(key) # scores.sort(key = lambda s: s[2]) print(scores) ``` --- ## 指定型別 ```python= def is_bigger_then_0(n): if n>0: return True else : return False ``` 可以改成 ```python= def is_bigger_then_0(n:int)-> bool : if n>0: return True else : return False ``` 可以指定傳入和傳出的型別,如果不一致則會回報錯誤 在有時候會有浮點數和整數的情況下 這也許能幫到你 --- ## 進階觀念 函式在python中當然也是物件 能賦值,也能作為引述傳遞,更進一步也能透過修飾器(decorator)來調整函式的功能 ex: ```python= def foo(p1,p2): return(p1+p2) im_a_function=foo #沒括號=不是呼叫 im_a_function(10,1) #>>> 11 ``` ex2 ```python= def build(): print("Best match!") def Ex_aid(): print("Level up!") def zio(rider): def armor(): print("Zi-O ") print("Armor time") rider() print(rider.__name__) return armor zio(build)() zio(Ex_aid)() ``` --- ### 修飾器 向上面一樣把function包裝就能用修飾器來達成 ex: ```python= def zio(rider): def armor(): print("Zi-O ") print("Armor time") rider() print(rider.__name__) return armor @zio def build(): print("Best match!") @zio def Ex_aid(): print("Level up!") build() Ex_aid() ``` 詳細內容請去看這位大大寫ㄉ [Python進階技巧 (3) — 神奇又美好的 Decorator ,嗷嗚!](https://medium.com/citycoddee/python%E9%80%B2%E9%9A%8E%E6%8A%80%E5%B7%A7-2-static-class-abstract-methods%E4%B9%8B%E5%AF%A6%E7%8F%BE-1e3b3998bccf)
{"metaMigratedAt":"2023-06-15T15:50:56.531Z","metaMigratedFrom":"YAML","title":"函式 - Python 教學","breaks":false,"description":"中興大學資訊研究社1091學期程式分享會主題社課","contributors":"[{\"id\":\"e86b6571-4dea-4aa4-ba20-ece559b0e015\",\"add\":20053,\"del\":13836},{\"id\":\"de8e7839-dcf2-4d44-a4b5-080015e10202\",\"add\":147,\"del\":145},{\"id\":\"4039c7c6-929e-4623-bcab-ee47f79a408c\",\"add\":2,\"del\":1},{\"id\":\"4c23290c-4304-45d6-9c21-163639f3ac69\",\"add\":4264,\"del\":1216}]"}
    1101 views
   owned this note