進階篇 第6章 函數
第6章 函數
6.1函數的創建和調用
提到函數,大家會想到數學函數吧,函數是數學最重要的一個模塊,貫穿整個數學學習過程。在Python中,函數的應用非常廣泛。在前面我們已經多次接觸過函數。例如,用於輸出的print() 函數、用於輸出的input() 函數及用於生成一系列整數的range() 函數,這些都是Python內置的標誌函數,可以直接使用。除了可以直接使用的標準函數外,Python還支持自定義函數。即通過將一段有規律的,重復的代碼定義為函數,來達到一次編寫、多次調用的目的。使用函數可以提高代碼的重復利用率。
6.1.1 創建一個函數
創建函數也稱為定義函數,可以理解為創建一個具有某種用途的工具。使用def 關鍵字實現,具體的語法格式如下:
def functionname([parameterlist]): [‘‘‘comments‘‘‘] [functionbody]
參數說明:
- functionname:函數名稱,在調用函數時使用。
- parameterlist:可選參數,用於指定向函數中傳遞的參數。如果有多個參數,各參數間使用逗號“,”分隔。如果不指定,則表示該函數沒有參數,在調用時也不指定參數。
註意:即使函數沒有參數,也必須保留一對空的“()”,否則將顯示下圖所示錯誤提示對話框。
語法錯誤對話框
- ‘‘‘comments‘‘‘:可選參數,表示為函數指定註釋,註釋的內容通常是說明該函數的功能、要傳遞的參數的作用等,可以為用戶提供友好提示和幫助的內容。
說明:……
- functionbody:可選參數,用於指定函數體,即該函數被調用後,要執行的功能代碼。如果函數有返回值,可以使用return語句返回。
註意:函數體“functionbody”和註釋“‘‘‘comments‘‘‘”相對於def關鍵字必須保持一定的縮進。
說明:如果想定義一個什麽也不做的空函數,可以使用pass語句作為占位符。
例如,定義一個過濾危險函數filterchar(),代碼如下:
def filterchar(string): ‘‘‘功能:過濾危險字符(如黑客),並將過濾後的結果輸出 about:要過濾的字符串 沒有返回值‘‘‘ import re # 導入Python的re模塊 pattern = r‘(黑客)|(抓包)|(監聽)|(Trojan)‘ # 模式字符串 sub = re.sub(pattern,‘@_@‘,string) # 進行模式替換 print(sub)
運行上面的代碼,將不顯示任何內容,也不會拋出異常,因為filterchar() 函數還沒有被調用。
6.1.2 調用函數
調用函數也就是執行函數。如果把創建的函數理解為創建一個具有某種用途的工具,那麽調用函數就相當於使用該工具。調用函數的基本語法格式如下:
functionname([parametersvalue])
參數說明:
- functionname:函數名稱,要調用的函數名稱必須是已經創建好的。
- parametersvalue:可選參數,用於指定各個參數的值。如果需要傳遞多個參數值,則各個參數間使用逗號“,” 分隔。如果該函數沒有參數,則直接寫一對小括號即可。
例如,調用在6.1.1 小節創建的filterchar() 函數,可以使用下面的代碼:
def filterchar(string):
‘‘‘功能:過濾危險字符(如黑客),並將過濾後的結果輸出
about:要過濾的字符串
沒有返回值
‘‘‘
import re # 導入Python的re模塊
pattern = r‘(黑客)|(抓包)|(監聽)|(Trojan)‘ # 模式字符串
sub = re.sub(pattern,‘@_@‘,string) # 進行模式替換
print(sub)
about = ‘我是一名程序員,喜歡看黑客方面的圖書,想研究一個Trojan。‘
filterchar(about)
調用filterchar() 函數後,將顯示如下結果。
我是一名程序員,喜歡看@_@方面的圖書,想研究一個@_@。
場景模擬:第4章的實例01實現了每日一貼功能,但是這段代碼只能執行一次,如果想要再次輸出,還需要再重新寫一遍。如果把這段代碼定義為一個函數,那麽就可以多次顯示每日一貼了。
實例01:輸出每日一貼(共享版)
def function_tips(): ‘‘‘功能:每天輸出一條勵誌文字 ‘‘‘ import datetime # 導入日期時間類 # 定義一個列表 mot = ["今天星期一:\n人生充滿了不確定,和驚喜。", "今天星期二:\n人生亦可燃燒,亦可腐敗,我願燃燒,耗盡所有的光芒。", "今天星期三:\n毅力和耐性在某種程度上將決定一個人會成為什麽樣的人。", "今天星期四:\n微笑擁抱每一天,做像向日葵般溫暖的女子。", "今天星期五:\n誌在峰巔的攀登者,不會陶醉在沿途的某個腳印之中。", "今天星期六:\n別小看任何人,越不起眼的人。往往會做些讓人想不到的事。", "今天星期日:\n我們可以失望,但不能盲目。"] day = datetime.datetime.now().weekday() # 獲取當期星期 print(mot[day]) # 輸出每日一貼 #*******************調用函數************************# function_tips() # 調用函數
運行結果如下:
今天星期六:
別小看任何人,越不起眼的人。往往會做些讓人想不到的事。
6.2 參數傳遞
在調用函數時,大多數情況下,主要函數和被調用函數之間有數據傳遞關系,這就是有參數的函數形式。函數參數的作用是傳遞數據給函數使用,函數利用接收的數據進行具體的操作處理。
函數參數在定義函數時放在函數名稱的後面的一對小括號中。
6.2.1 了解形式參數和實際參數
在使用函數時,經常會用到形式參數和實際參數,二者都叫作參數,它們的區別將通過形式參數與實際參數的作用來進行講解,再通過一個比喻和實例進行深入探討。
1.通過作用理解
形式參數和實際參數在作用上的區別如下:
- 形式參數:在定義函數時,函數名後面括號中的參數為“形式參數”。
- 實際參數:在調用一個函數時,函數名後面括號中的參數為“實際參數”,也就是將函數的調用者提供給函數的參數稱為實際參數。
根據實際參數的類型不同,可以分為將實際參數的值傳遞給形式參數和將實際參數的引用傳遞給形式參數兩種情況。其中,當實際參數為不可變對象時,進行值傳遞;當實際參數為可變對象時,進行的是引用傳遞。實際上,值傳遞和引用傳遞的基本區別就是,進行值傳遞後,改變形式參數的值,實際參數的值不變;而進行引用傳遞後,改變形式參數的值,實際參數的值也一同改變。
例如,定義一個名稱為demo 的函數,然後為demo() 函數傳遞一個字符串類型的變量作為參數(代表值傳遞),並在函數調用前後分別輸出該字符串變量,再為demo() 函數傳遞以下列表類型的變量作為參數(代表引用傳遞),並在函數調用前後分別輸出該列表。代碼如下:
# 定義函數 def demo(obj): print("原值:",obj) obj += obj # 調用函數 print("==========值傳遞==========") mot ="唯有在被追趕的時候,你才能真正地奔跑。" print("函數調用後:",mot) demo(mot) # 采用不可變對象——字符串 print("函數調用後:",mot) print("==========引用傳遞===========") list1 = [‘李白‘,‘杜甫‘,‘白居易‘,‘李清照‘] print("函數調用前:",list1) demo(list1) # 采用可變對象——列表 print("函數調用後:",list1)
上面代碼的執行結果如下:
==========值傳遞========== 函數調用後: 唯有在被追趕的時候,你才能真正地奔跑。 原值: 唯有在被追趕的時候,你才能真正地奔跑。 函數調用後: 唯有在被追趕的時候,你才能真正地奔跑。 ==========引用傳遞=========== 函數調用前: [‘李白‘, ‘杜甫‘, ‘白居易‘, ‘李清照‘] 原值: [‘李白‘, ‘杜甫‘, ‘白居易‘, ‘李清照‘] 函數調用後: [‘李白‘, ‘杜甫‘, ‘白居易‘, ‘李清照‘, ‘李白‘, ‘杜甫‘, ‘白居易‘, ‘李清照‘]
從上面的執行結果中可以看出,在進行值傳遞時,改變形式參數的值後,實際參數的值不改變;在進行引用傳遞時,改變形式參數的值後,實際參數的值也發生改變。
2. 通過一個比喻來理解形式參數和實際參數
函數定義時參數列表中的參數就是形式參數,而函數調用時傳遞進來的參數就是實際參數。就像劇本選主角一樣,劇本的角色相當於形式參數,而演角色的演員就相當於實際參數。
實例02:根據身高、體重計算BMI 指數(共享版)
def fun_bmi(person,height,weight): ‘‘‘功能:根據身高和體重計算BMI指數 person:姓名 height:身高,單位:米 weight:體重,單位:千克 ‘‘‘ print(person + "的身高:" + str(height) + "米\t體重:" + str(weight) + "千克") bmi=weight/(height*height) # 用於計算BMI指數,公式為:BMI=體重/身高的平方 print(person + "的BMI指數為:"+str(bmi)) # 輸出BMI指數 # 判斷身材是否合理 if bmi<18.5: print("您的BMI指數為:"+str(bmi)) # 輸出BMI指數 print("體重過輕 ~@_@~") if bmi>=18.5 and bmi<24.9: print("您的BMI指數為:"+str(bmi)) #輸出BMI指數 print("正常範圍,註意保持(-_-)") if bmi>=24.9 and bmi<29.9: print("您的BMI指數為:"+str(bmi)) #輸出BMI指數 print("體重過重 ~@_@~") if bmi>=29.9: print("您的BMI指數為:"+str(bmi)) #輸出BMI指數 print("肥胖^@_@^") # **********************************調用函數************************************* # fun_bmi("路人甲",1.83,60) # 計算路人甲的BMI指數 fun_bmi("路人乙",1.60,50) # 計算路人乙的BMI指數
運行結果如下。
路人甲的身高:1.83米 體重:60千克 路人甲的BMI指數為:17.916330735465376 您的BMI指數為:17.916330735465376 體重過輕 ~@_@~ 路人乙的身高:1.6米 體重:50千克 路人乙的BMI指數為:19.531249999999996 您的BMI指數為:19.531249999999996 正常範圍,註意保持(-_-) >>>
從該實例代碼和運行結果可以看出:
(1)定義一個根據身高、體重計算BMI指數的函數fun_bmi(),在定義函數時指定的變量person、height和weight稱為形式參數。
(2)在函數fun_bmi()中根據形式參數的值計算BMI指數,並輸出相應的信息。
(3)在調用fun_bmi()函數時,指定的“路人甲”、1.83和60等都是實際參數,在函數執行時,這些值將被傳遞給對應的形式參數。
6.2.2 位置參數
位置參數也稱必備參數,是必須按照正確的順序傳到函數中,即調用時的數量和位置必須和定義時是一樣的。
1. 數量必須與定義時一致
在調用函數時,指定的實際參數的數量必須與形式參數的數量一致,否則將拋出TypeError異常,提示缺少必要的位置參數。
2. 位置必須與定義時一致
在調用函數時,指定的實際參數的位置必須與形式參數的位置一致,否則將產生以下兩種結果。
- 拋出TypeError異常
拋出異常的情況主要是因為實際參數的類型與形式參數的類型不一致,並且在函數中,這兩種類型還不能正常轉換。
- 產生的結果與預期不符
在調用函數時,如果指定的實際參數與形式參數的位置不一致,但是它們的數據類型一致,那麽就不會拋出異常,而是產生結果與預期不符的問題。
說明:由於調用函數時,傳遞的實際參數的位置與形式參數的位置不一致時,並不會總是拋出異常,所以在調用函數時一定要確定好位置,否則產生Bug,還不容易被發現。
6.2.3 關鍵字參數
關鍵字參數是指使用形式參數的名字來確定輸入的參數值。通過該方式指定實際參數時,不再需要與形式參數的位置完全一致。只要將參數名字寫正確即可。這樣可以避免用戶需要牢記的參數位置的麻煩,適當函數的調用和參數傳遞更急靈活方便。
6.2.4 為參數設置默認值
調用函數時,如果沒有指定某個參數將拋出異常,為了解決這個問題,我們可以為參數設置默認值,即在定義函數時,直接指定形式參數的默認值。這樣,當沒有傳入參數時,則直接使用定義函數時設置的默認值。定義帶有默認值參數的函數的語法格式如下:
def functionname(...,[parameter1 = defaultvalue1]): [functionbody]]
參數說明:
- functionname:函數名稱,在調用函數時使用。
- parameter = defaultvalue1:可選參數,用於指定向函數中傳遞的參數,並且為該參數設置默認值為defaultvalue1。
- functionbody:可選參數,用於指定函數體,即該函數被調用後,要執行的功能代碼。
註意:在定義函數時,指定默認的形式參數必須在所有參數的最後,否則將產生語法錯誤。
……
6.2.5 可變參數
在Python中,還可以定義可變參數。可變參數也稱不定長參數,即傳入函數中的實際參數可以是任意多個。
定義可變參數時,主要有兩種形式:一種是*parameter,另一種是**parameter。
1. *parameter
這種形式表示接收任意多個實際參數並將其放到一個元組中。例如,定義一個函數,讓其可以接收任意多個實際參數,代碼如下:
……
2. **parameter
這種形式表示接收任意多個類似關鍵參數一樣顯示賦值的實際參數,並將其放到一個字典中。例如,定義一個函數,讓其可以接收任意多個顯示賦值的實際參數,代碼如下:
……
6.3 返回值
到目前為止,我們創建的函數都只是為我們做一些事,做完了就結束。但實際上,有時還需要對事情的結果進行獲取。這類似於主管向下級職員下達命令,職員去做,最後需要將結果報告給主管。為函數設置返回值的作用就是將函數的處理結果返回給調用它的程序。
在Python中,可以在函數體內使用return語句為函數指定返回值,該返回值可以是任意類型,並且無論return語句出現在函數的什麽位置,只要得到執行,就會直接結束函數的執行。
return語句的語法格式如下:
return [value]
參數說明:
- value:可選參數,用於指定要返回的值,可以返回一個值,也可以返回多個值。
為函數指定返回值後,在掉用函數時,可以把它賦給一個變量(如result),用於保存函數的返回結果。如果返回一個值,那麽result中保存的就是返回的一個值,該值可以為任意類型。如果返回多個值,那麽result 中保存的是一個元組。
說明:當函數中沒有return 語句時,或者省略了return 語句的參數時,將返回None,即返回空值。
……
6.4 變量的作用域
變量的作用域是指程序代碼能夠訪問該變量的區域,如果超出該區域,在訪問時就會出現錯誤。在程序中,一般會根據變量的“有效範圍”將變量分為“全局變量”和“局部變量”。
6.4.1 局部變量
局部變量是指在函數內部定義並使用的變量,它只在函數內部有效。即函數內部的名字只在函數運行時才會創建,在函數運行之前或者運行完畢之後,所有的名字就都不存在了。所以,如果在函數外部用函數內部定義的變量,就會出現拋出NameError異常。
6.4.2 全局變量
與局部變量對應,全局變量為能過作用於函數內外的變量。全局變量主要有以下兩種情況:
(1)如果一個變量,在函數外定義,那麽不僅在函數外可以訪問函數,在該函數內輸出全局變量message 的值,代碼如下:
……
說明:當局部變量與全局變量重名時,對函數體的變量進行賦值後,不影響函數體外的變量。
(2)在函數體內定義,並且使用global關鍵字修飾後,該變量也就變為全局變量。在函數體外也可以訪問到該變量,並且在函數體內還可以對其進行修改。
註意:盡管Python允許全局變量和局部變量重名,但是在實際開發時,不建議這麽做,因為這樣容易讓代碼亂,很難分清哪些是全局變量,哪些是局部變量。
6.5 匿名函數(lambda)
匿名函數是指沒有名字的函數,應用在需要一個函數,但是有不想費神去命名這個函數的場合。通常情況下,這樣的函數只有使用一次。在Python中,使用lambda表達式創建匿名函數,其語法格式如下:
result = lambda [arg1 [,arg2,......,argn]]:expression
參數說明:
- result:用於調用lambda表達式。
- [arg1[,arg2,......,argn]]:可選參數,用於指定傳遞的參數列表,多個參數間使用逗號“,”分隔。
- expression:必選參數,用於指定一個實現具體功能的表達式。如果有參數,那麽在該表達式中將應用這些參數。
註意:使用lambda表達式時,參數可以有多個,用逗號“,”分隔,但是表達式只能有一個,即只能返回一個值。而且也不能出現其他非表達式語句(如for 或 while)。
進階篇 第6章 函數