之前的文章我們簡單介紹了一下 Python 的幾種變量類型,本篇文章我們來看一下 Python 中的函數。
函數是組織好的,可重復使用的,用來實現單一,或相關聯功能的代碼段。
函數能提高應用的模塊性,和代碼的重復利用率。你已經知道Python提供了許多內建函數,比如print()。但你也可以自己創建函數,這被叫做用戶自定義函數。
你可以定義一個由自己想要功能的函數,以下是簡單的規則:
- 函數代碼塊以 def 關鍵詞開頭,后接函數標識符名稱和圓括號 ()。
- 任何傳入參數和自變量必須放在圓括號中間,圓括號之間可以用於定義參數。
- 函數的第一行語句可以選擇性地使用文檔字符串—用於存放函數說明。
- 函數內容以冒號起始,並且縮進。
- return [表達式] 結束函數,選擇性地返回一個值給調用方。不帶表達式的return相當於返回 None。
Python 定義函數使用 def 關鍵字,一般格式如下:
1 def 函數名(參數列表): 2 函數體
1 def getNum(): 2 pass
如果我們定義了一個函數,里面什么也沒有寫,為了不讓代碼崩潰,我們可以寫一個 pass。
現在我們來定義一個可執行的函數:
1 def getNum(): 2 print(123) 3 getNum() # 123
如上我們定義了一個 getNum 的函數,函數內部 print 輸出 123,當我們調用 getNum() 時,就會輸出 123。
如果我們不想輸出固定的 123,輸出結果有我們自己定義,我們可以通過傳參的形式解決:
def getNum(num): print(num) print(num * 2) getNum(123) # 123 246
可以通過 return 的方式把想要的結果 return 出來:
1 def getNum(num): 2 return num 3 print(getNum(123)) # 123
函數中也可以使用嵌套函數的方式:
1 def getNum(num): 2 getName(num) 3 4 def getName(name): 5 print(name) 6 7 getNum("zhangsan") # zhangsan
我們通過向函數傳值來獲取我們的結果,也可以通過設置變量的形式來解決:
num = 1 def getNum(): print(num) getNum() # 1
上面的代碼我們設置了一個全局變量,當然我們也可以在函數內部設置變量,叫做局部變量,這樣更有利於我們對自己函數內的變量操作而不去改變全局的變量,如下:
1 def getNum(): 2 name = "zhangsan" 3 print(name) 4 5 getNum() # zhangsan
在有的時候,我們可以直接將函數內的參數設置默認值,這就就可以避免在沒有傳入該參數時函數報錯:
1 def getNum(a, b, c=11, d=22): 2 print(a) 3 print(b) 4 print(c) 5 print(d) 6 7 getNum(1, 2, 3) # 1 2 3 22
在上面的代碼中,我們對函數 getNum 的第三個和第四個參數設置了默認值,在調用該函數時,我們傳入了三個值,然后輸出結果發現,第三個參數的默認值被調用 getNum 時傳入的參數值覆蓋掉了,而第四個參數值由於沒有傳入,所以使用了默認值 d=22。由此我們可以得出,當我們不傳入對應參數值時如果設置默認值則使用默認值,如果傳入了參數值則優先使用傳入的參數值。
在上面的代碼中,我們定義的參數都是單一的字符串或者數字,我們也可以定義列表,元組,字典這樣的參數,如下:
1 list = [11, 22, 33] 2 tuple = (44, 55, 66) 3 dict = {"a": "77", "b": 88, "c": 99} 4 5 def getTest(list, tuple, dict): 6 print(list) 7 print(tuple) 8 print(dict) 9 10 getTest(list, tuple, dict) 11 ''' 12 [11, 22, 33] 13 (44, 55, 66) 14 {'a': '77', 'b': 88, 'c': 99} 15 '''
在上面的代碼中我們可以看出,Python 中的不同變量類型都可以作為參數傳入函數中。
接下來我們再來看一下函數中缺省參數的 *args 和 **kwargs
1 def getTest(a, b, c=123, *args): 2 print(a) # 11 3 print(b) # 22 4 print(c) # 33 5 print(args) # (44, 55, 66) 6 7 getTest(11, 22, 33, 44, 55, 66)
在上面的代碼中,print(c) 結果為 33,這個我們再上面已經解釋過了,但是我們在參數 c 后面又加入了一個 *args 的形參,然后我們在調用 getTest() 時向函數內部傳入參數的個數為 6 個,而函數體 getTest() 接收的參數為 4 個,按我們上面叫的函數方法,理論上程序應該報錯,然而程序卻能運行,運行結果打印的第 4 個參數結果為一個元組,將形參按順序對應的參數對應完畢之后剩余的內容組成一個元組輸出。其中 *args 可以寫成 *xxx,我們習慣上命名為 *args。
在函數傳參時,除了 *args 外還有一個 **kwargs,如下:
1 def getTest(a, b, c=123, *args, **kwargs): 2 print(a) # 11 3 print(b) # 22 4 print(c) # 33 5 print(args) # (44, 55, 66) 6 print(kwargs) # {'name': 'zhangsan', 'age': 30} 7 8 getTest(11, 22, 33, 44, 55, 66, name="zhangsan", age=30)
上面的代碼中,我們在函數 getTest() 后面又多家了一個形參 **kwargs,當我們在調用 getTest() 函數並傳入參數時,我們傳入了 name="zhangsan" 這樣的鍵值對,這樣在打印輸出 **kwargs 時會轉為一個字典類型的數據。其中 **kwargs 可以寫成 **xxx,我們習慣上命名為 **kwargs。
注意:在print() 輸出時,*args 和 **kwargs 的 * 不需要寫。
根據上面的 *args 和 **kwargs,我們現在來向這樣一個問題,加入我先在外面分別定義了一個元組類型變量 tuple 和字典類型數據 dict,然后調用 getTest() 函數時將變量傳入,輸出結果還是跟上面的結果一樣,即下面的代碼:
1 def getTest(a, b, c=123, *args, **kwargs): 2 print(a) # 11 3 print(b) # 22 4 print(c) # 33 5 print(args) # ((44, 55, 66), {'name': 'zhangsan', 'age': 30}) 6 print(kwargs) # {} 7 8 tuple=(44,55,66) 9 dict={'name': 'zhangsan', 'age': 30} 10 getTest(11, 22, 33, tuple, dict)
當我們還是按照上面的 *args 和 **kwargs 寫法寫時,發現參數 tuple 和 dict 被當做一個數據輸出到了 *args 里,這是由於當我們傳入 tuple 和 dict 時被當做了一個整體,我們可以在傳入前先將其解構一下就可以了,如下:
1 def getTest(a, b, c=123, *args, **kwargs): 2 print(a) # 11 3 print(b) # 22 4 print(c) # 33 5 print(args) # (44, 55, 66) 6 print(kwargs) # {'name': 'zhangsan', 'age': 30} 7 8 tuple=(44,55,66) 9 dict={'name': 'zhangsan', 'age': 30} 10 getTest(11, 22, 33, *tuple, **dict)
如上,我們在傳入參數的時候傳入 *tuple 和 **dict 就可以解決了,但是如果我們在傳入參數時和調用時都不加 * 不就相當於把整個變量作為參數了嗎?如下:
1 def getTest(a, b, c=123, tuple, dict): 2 print(a) 3 print(b) 4 print(c) 5 print(tuple) 6 print(dict) 7 8 tuple=(44,55,66) 9 dict={'name': 'zhangsan', 'age': 30} 10 getTest(11, 22, 33, tuple, dict) # SyntaxError: non-default argument follows default argument
我們會發現不寫 * 的話會報錯,原因是當我們在函數中定義默認參數時,默認參數必須寫在最后面,即 c=123 需寫在最后面,如下:
1 def getTest(a, b, tuple, dict, c=123): 2 print(a) # 11 3 print(b) # 22 4 print(c) # 33 5 print(tuple) # (44, 55, 66) 6 print(dict) # {'name': 'zhangsan', 'age': 30} 7 8 tuple=(44,55,66) 9 dict={'name': 'zhangsan', 'age': 30} 10 getTest(11, 22, tuple, dict, 33)
但是如果參數中存在 * 的話默認參數不能寫在最后面,如下:
1 def getTest(a, b, *tuple, **dict, c=123): 2 print(a) 3 print(b) 4 print(c) 5 print(tuple) 6 print(dict) 7 8 tuple=(44,55,66) 9 dict={'name': 'zhangsan', 'age': 30} 10 getTest(11, 22, *tuple, **dict, 33) # SyntaxError: invalid syntax
由此我們得出函數的傳參順序為:參數,默認參數,*args,**kwargs。
我們接下來看一下匿名函數。
python 使用 lambda 來創建匿名函數。
所謂匿名,意即不再使用 def 語句這樣標准的形式定義一個函數。
- lambda 只是一個表達式,函數體比 def 簡單很多。
- lambda的主體是一個表達式,而不是一個代碼塊。僅僅能在lambda表達式中封裝有限的邏輯進去。
- lambda 函數擁有自己的命名空間,且不能訪問自己參數列表之外或全局命名空間里的參數。
- 雖然lambda函數看起來只能寫一行,卻不等同於C或C++的內聯函數,后者的目的是調用小函數時不占用棧內存從而增加運行效率。
lambda 函數的語法只包含一個語句,如下:
lambda [arg1 [,arg2,.....argn]]:expression
1 # 實現一個兩數相加的匿名函數 2 sum = lambda num1, num2: num1 + num2 3 print(sum(10, 20)) # 30