Functions

      在〈Functions〉中尚無留言

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年

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *