Python Function — 函式初探
為何要使用函式?
學習程式的過程中,剛開始會先理解一種程式語言的各種語法(syntax)、邏輯判斷方式等等,關於它們的性質及使用方式。
對於初學者來說目標只有一個:不論黑貓白貓,可以跑出結果的程式碼就最好。於是,大部分寫出來的程式,都會像流水帳似的一行行的平鋪直敘的將流程攤開來。
學習到了一定程度之後,就不會滿足於這種只求「溫飽」不種品質的程式撰寫方式。這時候的你,接觸到的程式不只是在自己的電腦上或者是練習平台上孤芳自賞的小功能,而是實際上會用到,提供其他人的應用服務。這時候的程式碼,或內容動則上百上千行,或與他人協同開發,於是就會希望程式碼能夠將問題切割成一小塊一小塊的處理,以及容易讓其他團隊成員理解與閱讀,並且可以重複使用。
函式就是以上問題的解藥。
函式可以讓我們重複使用同樣的程式碼,而不需要重複的撰寫。
觀察看看下面的程式碼,是不是有點憋腳?
june_days = 30
print("June has " + str(june_days) + " days.")july_days = 31
print("July has " + str(july_days) + " days.")
是不是發現有些地方同樣的程式碼不斷的出現?只要呼叫一個月份,就要重複寫一次,而且程式碼幾乎90%是一樣的。
是否有更好的方式來寫這段程式碼?
實際上是有的。在這種狀況下,改用函式來重構上面的程式碼,就可以不用像上面一樣,重複撰寫同樣的程式碼。
def month_days(month,days):
return f"{month} has " + str({days}) + " days."
開始重構時先定義一個名稱為month_days
的函式,以及兩個參數(month與days),讓我們任意輸入月份與日期數字。每次呼叫month_days
函式時,就會return回傳我們想要的結果。
一但完成了函式的建構,未來當我們有需要時,就可以呼叫它來執行任務,想呼叫幾次都可以:
june = month_days('June',30)july = month_days('July',31)
採用上面的方式,不但可以讓我們呼叫六月、七月,想要呼叫八月份,也可以輕鬆的完成:
august = month_days('August',31)
各位是否已經發現函式的魅力?
函式的基本語法
既然已經看到了函式的魅力,我們就可以開始學習如何撰寫。首先要暸解一下函式的基本語法。
下面是一般函式的撰寫方式:
def 函式名稱(參數1, 參數2, 參數3,...):
# 程式區塊
程式區塊
函式以def宣告開始,包含了「函式名稱、參數、程式區塊」。其中,函式名稱後面的參數並非必要選項,並不是每個函式都需要參數。因此你會看到有些函式名稱後面只帶一個空括號,而沒有參數。
函式名稱()
程式區塊需要縮排
函式在第一行名稱後面需要透過「冒號」結束並且換行開始程式區塊。程式區塊的部分需要縮排,可以使用「space鍵」空四格來縮排或者是使用「tab鍵」縮排。
三個雙引號包裹字串,說明函式用途
函式裡面可以加上由三個雙引號『"""
』包裹的字串,此字串可以撰寫說明函式的用途。
當未來你的程式碼中有很多函式的時候,透過這個方式就有助於快速分辨出每個函式的設計目的與功能。避免日後忘記當時是基於何種原因而設計這個函式。
def 函式名稱(參數1, 參數2, 參數3,...):
"""文字區塊,說明函式的用途"""
\\\\程式區塊
程式區塊
程式開發者可以透過help(函式名稱) 或者是 函式名稱.doc 來查看前面由三個雙引號包裹的字串。
help(函式名稱)
或者是:
函式名稱.__doc__
用Return回傳值
函式中,可以使用return敘述來將程式區塊運算出來的值傳給呼叫者,或指定給變數。如果沒有在函式裡面定義return敘述,則預設的回傳值是None(return None
)。
None在Python中是一種很特殊的資料型態,它指的是「空」或者是「無」。
def 函式名稱(參數):
# 程式區塊
return 回傳值
舉例
# 計算三角形面積函式
下面的triangle函式可以幫我們計算出三角形面積。只要輸入三角形的底(base)與高(height),triangle函式就會回傳三角形面積值:
def triangle(base, height):
"""計算三角形面積"""
return base* height/2
需要透過呼叫triangle
函式的方式使用它:
如果要求出底為5、高度為4的三角形面積,我們可以呼叫triangle函式,並帶入數字5與4作為參數。將結果指定給變數area_a。
# 三角形area_a面積
area_a = triangle(5,4)
又如,想要知道底為7、高度為3的三角形面積,我們可以指定一個變數area_b並且呼叫triangle函式,同時帶入7與3為參數。
# 三角形area_b面積
area_b = triangle(7,3)
這時,若想知道兩個三角形面積的總和,就變得很簡單。只要把area_a與area_b兩個變數相加(area_a + area_b
)就可以得到結果了。
# 加總兩個面積
sum = area_a + area_bprint(sum)
我們再看看下面的例子。
# 將時間換算為秒的函式
get_seconds
函式是將時間以秒計數,只要輸入時、分、秒,就可以獲得對應的總秒數:
def get_seconds(hours, minutes, seconds):
"""將時間換算為秒"""
return 3600*hours + 60*minutes + seconds
若想知道『三小時半又多10秒,到底是多少秒?』的時候,可以把三小時半又10秒換為3小時30分10秒,並且以3,30,10三個數值作為參數帶入get_seconds
函式,就可以得知:
time_a = get_seconds(3,30,10)print(time_a)
# 將秒轉換為時間的函式
反之,我們也可以用convert_seconds
函式取得秒數的時、分、秒:
def convert_seconds(seconds):
"""秒轉換為時間"""
hours = seconds # 60*60
minutes = (seconds - hours * 60*60 ) #60
remain_seconds = seconds - hours * 60*60 - minutes * 60
return hours, minutes, remain_seconds
呼叫函式的方式:輸入6000秒到convert_seconds
函式。convert_seconds
函式會回傳三個數字,分別指定給hours、minutes與seconds變數。
hours, minutes, seconds = convert_seconds(6000)
其他使用方式
在Python程式中,所有的資料,都是以物件或者其關係來表示。函式也不例外,它也是一種物件。不僅如此,而且它還是個「頭等物件」。因為函式可以指派給變數、存在資料結構中,函式也可以作為參數傳給其他的函式,它甚至可以作為其他函式的return值。
函式可以放在資料結構裡
既然函式是個頭等物件,函式也可以存在資料結構裡面。比如說存在串列(list)裡面。從串列中取用函式的方式就和一般串列取值一樣。
下面要試著把函式放在串列(list)裡面。首先定義一個func串列,再把前面提到的幾個函式(triangle
、get_seconds
、convert_seconds
)、放在下面的func串列中。
func = [triangle, get_seconds, convert_seconds]
在這個方式下,呼叫函式的方法:
sum = func[0](5,4)+ func[0](7,3)
其中,func[0]會呼叫出triangle函式。func0表示triangle(5,4)。
運算的結果是20.5。
# 三角形area_a面積
area_a = triangle(5,4)
# 三角形area_b面積
area_b = triangle(7,3)sum = area_a + area_b
print(sum)
與上面sum的結果一樣。
函式作為參數傳給其他函式
除了將函式放在資料結構裡面外,我們也可以將函式作為參數傳給其他函式。
下面的output函式可以傳入函式作為參數。我們可以試著把triangle函式傳入。
def output(func):
sum = func(5,4) +func(7,3)
print(sum)
把triangle函式作為output函式的參數傳入:
output(triangle)
結果可以得到一樣的答案:20.5。
由於我們可以把函式作為參數傳給其他函式,使得我們可以將程式的行為抽象化,這種方式也被稱為高階函式。
巢狀結構,在函式裡面放函式
我們也可以把函式放在另外的函式內形成一種巢狀的結構。例如下面的convert_text函式與lower_text函式之間的關係:
def convert_text(text1,text2):
def lower_text(t1,t2):
return t1.lower()+' '+t2.lower()
return lower_text(text1,text2)
lower_text是內部函式。呼叫外部函式convert_text會得到下面的結果。
convert_text('HELLO','WORLD')
# 例:網格系統函式
下面我們以Bootstrap的網格系統為例加以說明。假定要設計一個函式,這函式可以告訴我們應該使使用哪一個等級的網格系統。我們希望輸入瀏覽器的螢幕尺寸,就可以告訴我們該使用哪一個CSS Class的prefix。
好吧,我承認這純粹是個假設,對一般網頁設計師或前端工程師來說,這張表應該是倒背如流才對。就姑且當作要寫一支程式,讓機器人知道該如何對不同的螢幕尺寸給予不同的CSS prefix。
下面是Bootstrap官方文件上面的關於各種尺寸的表格。
我們依照上面表格規劃出get_grid_func函式,這個函式需要傳入螢幕的尺寸(size)作為參數:
get_grid_func函式會依照傳入的size值進行 if 條件式判斷,選出到底應該回傳哪個內部函式比較適當。當第一步驟確定對應的內部函式後,只要在第二步驟時傳入想要的column之後,函式就會自動回傳以row為單位的完整HTML原始碼。
def get_grid_func(size): def extra_small(grid): grid1= str(grid) grid2 = str(12 - int(grid)) row = f'<row><div class="col-{grid1}">lorem col 1</div><div class="col-{grid2}">lorem col 2</div></row>' return row def small(grid): grid1= str(grid) grid2 = str(12 - int(grid)) row = f'<row><div class="col-sm-{grid1}">lorem col 1</div><div class="col-sm-{grid2}">lorem col 2</div></row>' return row
def medium(grid): grid1= str(grid) grid2 = str(12 - int(grid)) row = f'<row><div class="col-md-{grid1}">lorem col 1</div><div class="col-md-{grid2}">lorem col 2</div></row>' return row def large(grid): grid1= str(grid) grid2 = str(12 - int(grid)) row = f'<row><div class="col-lg-{grid1}">lorem col 1</div><div class="col-lg-{grid2}">lorem col 2</div></row>' return row def extra_large(grid): grid1= str(grid) grid2 = str(12 - int(grid)) row = f'<row><div class="col-xl-{grid1}">lorem col 1</div><div class="col-xl-{grid2}">lorem col 2</div></row>' return row if size < 576: return extra_small elif 576 <= size < 768: return small elif 768 <= size < 992: return medium elif 992 <= size < 1200: return large else: return extra_large
接下來我們可以試著呼叫get_grid_func函式:比如說,想知道螢幕尺寸為480px應該使用哪個,可以先傳入480給 get_grid_func
函式。然後再傳入希望的欄( column)數 8。
//輸入尺寸480
screen_size = get_grid_func(480)
//輸入欄數8
screen_size('8')
結果我們會得到col-8與col-4。在網格系統裡面12為最大值,col-8加上col-4為12(8加4為12)。
輸出的HTML原始碼為:
<row>
<div class="col-8">lorem col 1</div>
<div class="col-4">lorem col 2</div>
</row>
又如想知道螢幕尺寸為1440px,第一欄為3的狀況,可以先傳入1440給get_grid_func
函式。然後再傳入希望的欄( column)數 3。
//輸入尺寸1440
screen_size = get_grid_func(1440)
//輸入欄數3
screen_size('3')
結果就會得到col-xl-3與col-xl-9,3加9為12。