###### [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())
```
---

遞迴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 輾轉相除法

---
::: 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}]"}