跳到主要內容

Python筆記(7)-函數的建立

我們在撰寫程式過程中,常會希望有現成的函數可以達到我們想要的功能,但有時偏偏沒有或者有時在程式中多次用到某些功能,此時我們可以將這些功能自己寫成函數,以後要使用的時候直接呼叫即可,一方面可以縮短程式長度,另一方面程式可讀性也會變好,維護上也相對容易。

7-1 函數的建立

Python函數建立的語法如下:

def 函數名稱(參數1, 參數2, 參數3,…):

        程式區塊

        return 回傳值1, 回傳值2, 回傳值3,…

<注意事項>

1.     函數可以有多個參數亦可沒有參數。

2.     若函數需要有回傳值,則必須在程式區塊加上return。函數可以有多個回傳值亦可沒有回傳值。有回傳值則可將結果指定給變數。

3.     如直接輸入參數值需注意順序,若擔心輸入順序錯誤,則可在輸入參數時加上參數名稱,此時就不需要擔心順序問題

4.     若希望有些參數為必填,則可以將該參數設定預設值,設定方式為在設定參數時指定數值且必須放在參數的最後面

程式碼

#建立函數(1)(輾轉相除法-最大公因數)

def gcd(x,y):

    if x==y:

        return x

    else:

        while x!=y:

            if x>=y: x=x-y

            if x<y : y=y-x

        return x

print(gcd(42,36))

Python畫面

程式碼

#建立函數(2)(BMI)

def BMI(weight,height):

    return weight/((height/100)**2)

w=75;h=183

print("75Kg,183公分,BMI{}".format(BMI(weight=w,height=h)))

Python畫面

程式碼

#建立函數(3)(找取方程式的根-牛頓法)

def gradient_decent_1d(f,df,x=1,acc=1e-03,detail=False):

    times=1

    x2=x-eval(f)/eval(df)

    if detail==True:print("step {}:{}".format(times,x2))

    while abs(x2-x)>=acc:

        times+=1

        x=x2; x2=x-eval(f)/eval(df)

        if detail==True:print("step {}:{}".format(times,x2))       

    return "root is {} ; accurancy is {}".format(x2,acc)

f1="x**2-2" ; df1="2*x"

print("使用(不修改)預設值".center(30,"="))

print(gradient_decent_1d(f=f1, df=df1))

print("修改預設值detailTrue".center(30,"="))

print(gradient_decent_1d(f=f1, df=df1, detail=True))

print("修改預設值detailTrueacc=1e-07".center(30,"="))

print(gradient_decent_1d(f=f1, df=df1, detail=True,acc=1e-07))

Python畫面

以上例子我們給了三個預設值,其中acc(精確度)預設為1e-03;detail(牛頓法執行細節)預設為False。當不修改預設值時,只需數入fdf,結果會直接回傳方程式的根與所採用的精確度。當修改預設值時,回傳的結果也會有相對應的修改。

<注意>函數的變數有預設值時需注意的陷阱,參考此文

7-2 變數使用的範圍-區域變數(Local Variable)與全域變數(Global Variable)

在許多程式語言中都有區域變數與全域變數的概念,在Python中,在函數內定義的變數稱為區域變數;在函數外定義的變數稱為全域變數。兩者無法互相變更值,即使變數名稱設定一樣。以下用一個簡單範例說明。

程式碼

#變數使用範圍(1)

x1=1  #全域變數

def test():

    x1=100  #區域變數

    print("區域變數x1:{}".format(x1))

test()

print("全域變數x1:{}".format(x1))

Python畫面

我們可以看到函數中的x1=100並未將函數外的x1=1修改,故函數內的變數不會影響函數外的變數。但是,如果真的有需求要在函數內使用函數外的變數,就必須將函數內的變數宣告成全域變數,寫法就是在變數前面加上global就可以在函數內使用函數外的變數。

 程式碼

#變數使用範圍(2)

x1=1  #全域變數

def test():

    global x1,x2 

    x1=100

    x2="Hello"

    print("函數內變數x1:{}".format(x1))

    print("函數內變數x2:{}".format(x2))

test()

x2="Welcome"

print("函數外變數x1:{}".format(x1))  

print("函數外變數x2:{}".format(x2))    

Python畫面

程式碼

#區域變數與全域面數的應用-猜數字遊戲

import random

def suggest_num_game():

    global Min,Max

    target=int(random.random()*100)

    guess=int(input("===目標在{}~{}之間===".format(Min,Max)))

    while True:

        if guess==target:

            print("答對了~ 答案是{}".format(target));break

        else:

            if guess<target:Min=guess

            if guess>target:Max=guess

            guess=int(input("===目標在{}~{}之間===".format(Min,Max)))

Min=int(input("請輸入猜測數字範圍最小值..."))

Max=int(input("請輸入猜測數字範圍最大值..."))

suggest_num_game()

Python畫面

7-3 函數無限參數的設定

在參數設定時,前面附帶一個*號,則代表可以無限輸入參數,並且會將輸入的參數打包成tuple型態供函數內部程式使用;若是前面附帶**號,亦可代表可以無限輸入參數,但會將輸入的參數打包成dict型態供函數內部程式使用。以下用簡單的例子說明一下:

程式碼

#函數參數的進階設定(1)-自訂平均數函數

def avg(*num):

    a=0

    for n in num:

       a+=n/len(num)

    return a

print(avg(1,2,3,4,5,6))

print(avg(1,1,1))

print(avg(-2,-1,1,2))

print(avg())

Python畫面

程式碼

#函數參數的進階設定(2)-輸入個人資料

def personal_list(**detail):

    print("{:<10s}{:<10s}{:<10s}{:<10s}".format(detail.get("Name"),detail.get("Job"),detail.get("Age"),detail.get("Birth")))   

print("{:<10s}{:<10s}{:<10s}{:<10s}".format("Name","Job","Age","Birth"))

personal_list(Name="ShawnH",Job="Engineer",Age="34",Birth="1988/11/18",Gender="Male")

Python畫面

最後,在函數的訓練當中,最有名的莫過於河內塔問題了,這個問題牽扯到遞迴數列的觀念,所以如何在程式中用函數寫出遞迴的效果也是一個重要的學習知識,這就是為什麼講到函數都會帶河內塔問題的訓練了。以下我們用上面學到的知識來寫出河內塔問題的解。

程式碼

#河內塔問題

def Hanoi(A,B,C,disk=2): 

    if disk==1: print("{}移到{}".format(A,C),end="")

    if disk>1:

        Hanoi(A,C,B,disk=disk-1)

        print("{}移到{}".format(A,C),end="")

        Hanoi(B,A,C,disk=disk-1)

Hanoi("A","B","C",disk=4)

Python畫面

留言

這個網誌中的熱門文章

RPA-Uipath筆記(1) - 如何從網路上下載資料並存至指定路徑

最近在做 RPA 相關專案時遇到了這個問題: 如何從網路上下載資料並存至指定路徑? 針對這個問題我們採取的解決方案是 先讓檔案下載到電腦的預設路徑,再將檔案移動到我們指定的資料夾 。 以下我們用下載政府公開資料平台的資料 ("https://data.gov.tw/dataset/116285") 為例, Uipath 的流程建立如下: 使用到了 Wait for Download 、 Move File 、 Delete File 三個 Activity , Wait for Download 中 Monitored folder 必須放瀏覽器中設定的下載檔案的預設路徑且關閉詢問儲存位置。 Downloaded file 則是設定一個變數名稱 downloadfile( 可自訂 ) ,此變數的資料型態是 FileInfo 。中間則放入下載的流程步驟,從下載開始到結束都會在此 Activity 中執行完畢,執行完後所下載的檔案的相關資訊都會被存放在變數 downloadfile 中。 接下來是將檔案從預設下載路徑中移至指定路徑,這裡就需要下載預設路徑與指定路徑,這兩個我們在流程一開始就建了了兩個變數 dnlo_path 、 asng_path ,寫法如下: dnlo_path = "C:\Users\" +system.Environment.UserName+ "\Downloads" system.Environment.UserName à VB 語法,代表目前電腦的使用者名稱。 asng_path = "D:\Bolg 資料 \Uipath\Uipath(1)" Move File Activity 中 From 要放入目前檔案的位置; To 則要放入檔案存放的指定的位置,但因為是要做檔案的移動,所以以上兩個路徑後面都要再加上檔案名稱,這時可以使用 downloadfile 這個變數的屬性值叫出檔案名稱( downloadfile.Name ),當檔案名稱是隨機變動的時候,非常好用。另外,特別值得注意的是, To 的部分在加上檔案名稱時,可以不用用原始檔案的名稱,這時可以同時達到修改檔名的效果。 Overwrite 的部分打勾代表再重複執行一次的時...

RPA-Uipath(2) - 字串分割(String.Split())與合併(String.join())

在資料清理中,我們常常需要對字串進行分割或合併,今天來簡單介紹 Uipath 中字串如何進行分割與合併。 1.      字串分隔 ( 單一分割符號 ) 我們首先建了一個字串變數 Str1= "Jimmy,Johnson,Shawn,Alan,Nick" ,我們現在想把這些人名一個一個切割出來,很明顯只要將 ”,” 作為分個符號就可以了。這邊我們建立一個陣列變數 Str1_Array=Str1.split(","c) ,這個陣列變數就會儲存分割出來的結果。我們接下來用 For each 與 Write Line 將陣列的每一項內容印出來,就可以知道我們分割的結果有沒有成功。 2.      字串分隔 ( 多個分割符號 ) 我們首先建了一個字串變數 Str2= "Jimmy,Johnson.Shawn Alan,Nick" ,我們現在想把這些人名一個一個切割出來,很明顯要將 ”,” 、 ”.” 、 ” “ 都作為分個符號才能完整分割。這邊我們建立一個陣列變數 Str2_Array= Str2.Split(",|.| ".ToArray()) or Str2.Split({","c,"."c," "c}) ,這個陣列變數就會儲存分割出來的結果。我們接下來用 For each 與 Write Line 將陣列的每一項內容印出來,就可以知道我們分割的結果有沒有成功。 3       字串的合併 (Array 字串元素合併 ) 我們首先建了一個字串變數 Str3_Array= {"John","Jimmy","Shawn","Alan"} ,我們現在想把這些人名中間用 ”|” 符號隔開並且變成一個字串,這裡我們是用 String.Join( 連接符號 , 陣列 ) 這個函數達到這個目的。我們建立一個變數 Str3_Join ,使 Str3_Join=String.Join("|", Str3_Array) ,此時 Str3_Join 的結果就會是 John|Jimmy|Shawn|Alan 。我們接下來用 Write...

RPA-Uipath(5)-如何讀取Mail與取得Mail的資訊

本篇主要介紹 Uipath 取得 mail 相關資訊與附件的方法,因為 mail 的種類眾多,本範例以 Gmail 為例。首先在使用 Uipath 取得 Gmail 相關資訊前須對 Google 帳戶的安全性進行兩階段驗證設定,設定方式如下: Step1. 點選 ” 管理你的 Google 帳戶 ” Step2.  點選 ” 安全性 ” à ” 兩階段驗證 ” à 點選 ” 開始使用 ” Step3.  依 Google 所提示步驟,您最後會取得一組密碼,請務必將此密碼記下。 接下來要開始使用 Uipath 擷取 mail 相關資訊,一開始我們先使用 Get Password 儲存 mail 登入的密碼,此時密碼不是使用您真正的密碼而是使用剛剛兩階段驗證後取得的密碼,並儲存於變數 password 。 接下來使用 Get IMAP Mail Messages 來取得 mail 的相關資訊,在使用 Get IMAP Mail Messages 時必須在其屬性設定區塊進行一些參數設定。 Port 欄位填入 993 , Server 欄位填入 "imap.gmail.com" , Email 欄位填入您的 Email , Password 欄位填入 變數 password , Message 欄位我們用 MailMessage 變數填入 Mail 的相關資訊都會存在 MailMessage 變數中,我們可以用 For Each 迴圈逐一取得 mail 的相關資訊。 For Each 的屬性區塊部分也需要進行一些參數設定, List of item 欄位需填入 MailMessage 變數, TypeArgument 則須設定為 System.Net.Mail.MailMessage 。 設定完成後,我們先用 Write Line 將信件的主旨印出做為測試( 這邊我們只印出前 5 封信件的主旨,可在 Get IMAP Mail Messages 的屬性區塊中 Top 欄位中設定 ),如果要印出信件主旨,需要用 item.Subject 屬性,如要印出其他資訊,我們將常用的幾個列在以下表格: 最後,有些信件會附帶一些附件,我們如何將這些物件儲存下來呢 ? 這邊只需要用到 Save Attachments 即可。我們將其放在迴圈內以下載每個有附件的 mail...