Python 從入門到進階之路(四)

豐寸發表於2019-06-13

之前的文章我們簡單介紹了一下 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

 

相關文章