1. 程式人生 > >Python基礎—函式

Python基礎—函式

1. 建立函式

一個函式代表一個行為並且返回一個結果(包括None),在Python中使用def關鍵字來定義一個函式,如下:

def hello(name):
    print 'hello,' + name + '!'

 接下來呼叫函式,並檢視其返回值:

# output:
# hello,gy!
# None
print hello('gy')

可以看到hello函式首先列印了:Hello,gy!,然後我們又將其返回值也打印出來,在這裡因為沒有返回具體的值,所以返回了None

 

下面我們可以定義一個用於計算斐波那契數列的函式,接收計算的位數(引數),返回計算的結果(返回值),如下:

複製程式碼
1 def fibs(num):
2     result = [0,1]
3     for i in range(num - 2):
4         result.append(result[-2] + result[-1])
5     return result
6 
7 # output: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
8 print fibs(10)
複製程式碼

 

2. 文件字串

文件字串指的是在函式開頭寫下該函式的說明字串,該字串會作為函式的一部分進行儲存。可以通過呼叫__doc__屬性來檢視:

複製程式碼
1 def fibs(num):
2     "根據指位數來計算斐波那契數列"
3     result = [0,1]
4     for i in range(num - 2):
5         result.append(result[-2] + result[-1])
6     return result
7 
8 # 輸出:根據指位數來計算斐波那契數列
9 print fibs.__doc__
複製程式碼

 

3. 引數

函式使用引數來傳遞資訊,而引數型別又基本分為兩大型別:可變型別參考和不可變型別引數。

 

3.1 不可變型別引數

不可變型別(字串、數字和元組等)是不可變的,即無法修改,只能使用新的值來覆蓋。使用不可變型別作為函式的引數時:在函式內為引數賦值不會改變其外部變數的值,如下:

複製程式碼
 1 # 字串是不可變的
 2 def try_to_change(n):
 3     n = 'Gumby'
 4 
 5 name = 'Sunshine'
 6 
 7 # 嘗試改變引數的值
 8 try_to_change(name)
 9 
10 # output: Sunshine(函式內部的改變對其沒有影響)
11 print name
複製程式碼

 

3.2 可變型別引數

可變型別引數(列表等)指的是:使用可變的資料結構來作為函式的引數使用。在函式內部修改可變型別引數的值時,會同時改變其外部變數的值(因為它們引用的其實是同一個物件),如下所示:

複製程式碼
 1 # 列表是可變的
 2 def try_to_change2(n):
 3     n[0] = 'Sunshine'
 4 
 5 person = ['Gumby','Bob']
 6 # 嘗試改變引數的值
 7 try_to_change2(person)
 8 
 9 # output: ['Sunshine', 'Bob']
10 # 函式內部的改變會作用於外部的變數
11 print person
複製程式碼

 

如果想避免上面的情況可以對需要作為可變型別的引數的物件複製一個副本,這裡是列表可以通過對其進行切片來返回一個新的副本,如下:

複製程式碼
 1 # 將可變的資料結構(列表)用作引數
 2 def try_to_change2(n):
 3     n[0] = 'Sunshine'
 4 
 5 person = ['Gumby','Bob']
 6 n = person[:] # 通過切片返回新的副本
 7 try_to_change2(n)
 8 print person # ['Gumby', 'Bob']
 9 print n # ['Sunshine', 'Bob']
10 print n is person # False
複製程式碼

 

3.3 關鍵字引數和預設值

在我們之前使用引數的形式是:位置引數,即呼叫該函式給其傳值時,是根據輸入的值的先後順序來給引數一一賦值 的。不過有時候如果我們需要定義的函式有大量的引數時,傳值時只是於依賴於順序的話是比較容易出錯的。同時如果部分引數在不多數情況下值都是一樣的,每次 呼叫都需要重新賦值也比較麻煩。針對於這種情況Python為我們提供了兩個語法糖:即關鍵字引數預設值。(PS:C#4.0新增的兩個特性:具名引數和可選引數,和Python的這兩個語法糖很類似,感興趣的同學可以參考這裡:《Effective C#》讀書筆記——條目10:使用可選引數減少方法過載的數量<C#語言習慣>)。

使用關鍵字引數不需要對函式進行任何修改,只需要在呼叫函式時顯示為引數賦值即可(不依賴特定順序),如下:

1 def hello(gretting,name):
2     print "%s,%s!" % (gretting,name)
3 
4 # output: hello,world!
5 hello(name='world',gretting='hello')

 

可以看到雖然呼叫時程式碼變多了,但是每個引數的含義一目瞭然。不過有時候我們的函式有些引數在大部分情況下使用同樣的值,有時候也需要改變,這時候使用預設值,可以很好的解決這種問題,如下:

複製程式碼
1 def hello(gretting = 'hello',name='world'):
2     print "%s,%s!" % (gretting,name)
3 
4 hello() # hello,world!
5 hello(name = 'Sunshine') # hello,Sunshine!
6 hello('Greetings') # Greetings,world!
複製程式碼

 可以看到,如果沒有給任何值的話,函式自動使用預設值,也可以通過關鍵字引數指定部分值,其他的依然使用預設引數,這為我們的方法呼叫提供了很大的靈活性。

 

3.4 使用任意引數列表

前面介紹的引數的個數基本都是固定的,有時候可以接收任意多的引數也是很有必要的,這時候可以使用Python為我們提供的語法糖(*號)來實現:

複製程式碼
 1 def print_params(number,*params):
 2     print number
 3     print params
 4 
 5 # output: 
 6 # 6
 7 # (1, 2, 3, 4, 5, 6)
 8 print_params(6,1,2,3,4,5,6)
 9 
10 # output:
11 # 0
12 # ()
13 print_params(0)
複製程式碼

 

我們可以看到引數前的*號將所有的值放置到一個元組中(可以理解為將其他的引數收集起來)。如果沒有為*號後的引數賦值則其值為一個空的元組。不過使用一個*號這樣的語法不能處理關鍵字引數,如下:

1 def print_params(*params):
2     print params
3 
4 # TypeError: print_params() got an unexpected keyword argument 'params'
5 print_params(params = (1,2,3))

 

解決的辦法很簡單,使用**來修飾引數即可,如下所示:

複製程式碼
1 # -- coding: utf-8 --
2 def print_params(**params):
3     print params
4     print type(params)
5 
6 # output: {'params': (1, 2, 3)}
7 # output: <type 'dict'>
8 print_params(params = (1,2,3))
複製程式碼

 可以看到使用**修飾的引數本質上其實是一個字典型別。可以通過這個字典來收集引數。

 

3.5 解包引數列表

除了通過才定義函式的引數(形參)前新增*或者**來收集引數,我們還可以在呼叫函式時在實參前調解這兩個操作符,它們表示其對應的逆過程。如下所示:

複製程式碼
1 def add(x,y):
2     print x + y
3 
4 params = (1,2)
5 
6 # output: 3
7 add(*params)
複製程式碼

 

通過使用*操作符我們將params引數分割了add函式所需的兩個引數,同樣我們也可以使用**操作符來分割字典物件:

複製程式碼
1 def hello(greeting,name):
2     print '%s,%s' %(greeting,name)
3 
4 params = {'greeting':'hello','name':'world'}
5 
6 # output: hello,world
7 # 這裡使用*操作符也是可以的
8 hello(**params)
複製程式碼