Functions
有的人翻譯為函數, 有的人稱為函式, 英文叫Functions. 函數是Python的靈魂, 其抽象化的思維, 讓函數產生了五花八門的變化, 不好學, 但功能極強. 這是在一般強型語言中所看不到的.
抽象化
人類常常使用抽象的名詞來表達某種意思. 比如高興, 悲傷, 這只是人的一種感覺, 無法量化. 所以在教小朋友這種心裏的感覺時, 往往要花上一段比較長的時間才教的會.
偏偏數學中, 抽象是常用到的概念. 比如計算 1+2+3+…+100, 這種計算數列的和, 寫起來相當不方便, 於是數學家就發明了∑ 符號, 寫成如下
100
∑n
n=1
上述符號就是一種抽象化的觀念, 這種記法非常強大, 因為我們可以繼續擴展成如下
100
∑(n2+1)
n=1
還原後就為如下
(1 x 1 + 1) + (2 x 2 + 1) + (3 x 3 + 1) + … + (100 x 100 + 1)
借由抽象化, 我們就不用關心計算的過程, 只需關注在問題的思考上. 太多數的人, 對抽象通常較難理解, 這也是為什麼那麼多人數學怎麼學也學不會的原因.
歹勢吼, 函數就是程式碼中最基本的抽象化產物.
內建函數
Python提供很多內建的函數供我們使用. 這是個好消息, 因為我們只需知道調用什麼函數, 傳入什麼參數, 就可得到什麼結果.
但壞消息呢?? 我那有那麼多的腦容量去記得那麼多的函數跟參數啊!!
還好, 可參考如下網
https://docs.python.org/3/library/functions.html
常見的函數如 abs, int, float, str, max, hex
置換函數名稱
a=abs
print(a(-100))
老實說, 一開始會覺的怎麼會有這麼智障的設計. 原來, 這是為了把函數傳入參數而設計的. 也就是函數指標的觀念. 待日後再解說.
自訂函數
需使用def定義自訂函數, 如下所示, 輸入函數名, 參數, return值. 特別注意
1. 需定義於使用之前, 否則會出現未定義的函數錯誤
2. 沒有返回值時, 可以不用寫return, 也可寫成, return None, 或return即可
def circle(r): return r*r*3.14159 print(circle(10))
引入自行定義的函數檔
如果把一些常用的函數寫在某個 .py檔, 比如在mahalsdk.py 裏有 def circle(x)函數.
那在新的代碼中可以使用from mahalsdk import circle;
from mahalsdk import circle print(circle(10))
Pass
pass是一個佔用符, 什麼都不作的意思,
比如如下的area函數, 什麼都不作, 是目前還不想寫, 等到有空再回來寫這段函數
又如if age<18: 如果沒有pass, 則會出錯
def area(r): pass age=20 if age<18: pass else: print("已成年")
參數型態
不是說了嘛, Python是弱型語言, 不需指定變數型態的. 但在這裏, 又開始矛盾了. 比如下面代碼, 我們輸入了adult(20), 當然沒問題. 但如果輸入了 adult(“test”), 則因為x是字串, 無法與18作比較運算. 所以就會發生執行時期錯誤.
def adult(age): if age < 18: pass else: print("已成年") adult(20)
所以在比較正式的函數中, 還需先作型別的判斷
型別的判斷, 可以使用isinstance(變數, (型別, 型別, 型別)), 如果變數是型別其中之一, 就會傳回True, 否則傳回False. 所以代碼改成如下
def adult(age): if not isinstance(age, (int, float)): raise TypeError('bad operand type') if age < 18: pass else: print("已成年") adult("test")
多值返回
如下代碼, 說明了返回二個值, 但其實只是返回tuple的資料型態
下面代碼需注意的事項如下
1. import math : 可調用常用的數學函數
2. math.cos, math.sin所傳入的參數為弳度, 角度與弳度的換算公式為
弳度 = 角度*π/180
角度 = 弳度*180/π
3. math.pow(9, 1/2) 則是計算9的開根號
import math def point(radius, angle=0): x = radius * math.cos(angle*math.pi/180) y = radius * math.sin(angle*math.pi/180) return x, y p=point(10,30) x=p[0] y=p[1] print("x:%f, y:%f" % (x, y)) print(math.pow(math.pow(x,2)+math.pow(y,2), 1/2)) print(math.pi) 結果 : x:8.660254, y:5.000000 10.0 3.141592653589793
變數生命周期
先看一段代碼如下
def test():
x=10
print(f'函數內的x:{x}')
test()
print(f'函數外的x:{x}')
此時函數外是沒有x的,因為x一離開函數,立即死亡。
再看下一段代碼
def test():
x=10
print(f'函數內的x:{x}')
x=100
test()
print(f'函數外的x:{x}')
結果 :
函數內的x:10
函數外的x:100
函數內外變數名稱相同時,是不同的個体
def test():
#x=10
print(f'函數內的x:{x}')
x=100
test()
print(f'函數外的x:{x}')
結果 :
函數內的x:100
函數外的x:100
main區的變數屬於全域變數,所以函數內沒有指定的話,也可以使用
良好的習是把main的全域變數寫在最上面
x=100
def test():
#x=10
print(f'函數內的x:{x}')
test()
print(f'函數外的x:{x}')
global變數
在函數內若加入 global x,則表示要使用main區的x
x=100
def test():
global x
x=10
print(f'函數內的x:{x}')
test()
print(f'函數外的x:{x}')
預設參數
上述定義函數時, 參數 angle=0, 表示調用此函數時, 可以不傳入angle這個參數. 若不傳入, 則會以 0為其預設值. 而有預設值的參數一定要放在無預設值參數之後, 不能被在前面, 比如
def point(angle=0, radius) 則是錯誤的定義法
如果有二個預設參數如下
def point(x, y, angle=0, step=0)
調用時, 可以依序填入 point(10,20, 30), 此時angle為 30, step則為0
也可以不依順序填入, point(10, 20 step=40), 此時angle為0, step為40
List參數
參照如下代碼, 第一次test(), 因為沒傳入list, 所以產生一個list物件, 並指定參考給l, 然後加入”end”物件.
第二次test(), 同樣沒傳入list, 所以l 還是指向原來的物件, 因為會第二次加入 “end”, 變成有二個”end”.
def test(l=[]): l.append("end") return l l=test() l=test() print(l)
為了解決此問題, 可以使用None, 如下
def test(l=None): if(l is None): #也可以使用 if(l == None): l=[] l.append("end") return l l=test() l=test() print(l)
可變參數
在調用函數時, 比如 cacl(1,2,3), 有3個參數, 可以計算1+2+3.
但如果cacl(1,2,3,4,5), 有5個參數, 計算 1+2+3+4+5的值
上述的函數, 該如何定義呢, 如下代碼所述, 在義函數時, 參數前加上 “*” 即可. 系統會直接在函數參數轉換成 tuple格式接收
def cacl(*n):
sum=0
for i in n:
sum+=i
return sum
print(cacl(1,2,3,4,5,6,7,8,9,10))
在調用函數時, 如果是tuple或list時, 該如何作呢, 如下
x=[1,2,3] cacl(x[0], x[1], x[2])
這樣的寫法又太過煩雜, 所以調用函數時, 也可以在list 或 tuple前面加入 “*”, 變成可變參數傳入
x=[1,2,3] cacl(*x)
遞迴(Recursive)
遞迴重點
1. 遞迴(Recursive)的定義 : 自已呼叫(調用)自已,但不是自已
2. 很多東西,沒有Recursive作不出來
3. 不會唸 Recursive 的人,不用進入這行業
4. Recursive非常浪費記憶体, 也很容易出現stack overflow的現象
5. 一定要有一個最終返迴值,否則會一直無限的計算下去,最後用光記憶体
6. list結構,二元樹結構,一定會用到Recrusive
舉個例子, 比如想計算 n!=n*(n-1)*(n-2)*…..*3*2*1的結果, 除了使用迴圈計算外, 亦可使用遞迴讓整個程式碼更加精簡.
def f(n): if n==1: return 1 else: return n*f(n-1) print(f(5));
時間膨脹公式
當物体有移動速度時,移動物本身的時間就會凍結。也就是說移動物本身經歷了1秒,但對於靜止的人而言,時間是大於1秒的。時間膨脹公式如下
$(r=\frac{1}{\sqrt{1-\frac{v^{2}}{c^2}}})$
此公式稱為勞侖茲因子。代碼如下
def tp(v):
c=299792458#光速 m/s
r=1/pow(1-(v*v)/(c*c), 0.5)
return r
def ms2kmh(v):
return v/1000*3600
def second2day(t):
return t/86400
def second2year(t):
return t/86400/365
v=299792450
print('時速 {:,} km/hr'.format(ms2kmh(v)))
r=tp(v)
s=r*86400
print(f'一天等於{s}秒')
print(f'一天等於{second2day(s)}天')
print(f'一天等於{second2year(s)}年')
結果 :
時速 1,079,252,820.0 km/hr
一天等於373993544.0531377秒
一天等於4328.628982096501天
一天等於11.859257485195894年