python學習之--自定義函數:


Python之--自定義函數

在Python中,定義一個函數要使用def語句,依次寫出函數名、括號、括號中的參數和冒號:,然后,在縮進塊中編寫函數體,函數的返回值用return語句返回。

以下自定義一個函數用於判斷一個人是成年人好事青少年:

 1 >>> def judge_person(age):
 2 ...     if age < 18:
 3 ...         print("teenager!")
 4 ...     else:
 5 ...         print("adult!")
 6 ... 
 7 >>> judge_person(12)
 8 teenager!
 9 >>> judge_person(23)
10 adult!
11 >>> 

請注意,函數體內部的語句在執行時,一旦執行到return時,函數就執行完畢,並將結果返回。因此,函數內部通過條件判斷和循環可以實現非常復雜的邏輯。

如果沒有return語句,函數執行完畢后也會返回結果,只是結果為None

return None可以簡寫為return

 1 >>> def get_age(age):
 2 ...     if age > 18:
 3 ...         return "older"
 4 ...     else:
 5 ...         return 
 6 ... 
 7 >>> get_age(12)
 8 >>> get_age(56)
 9 'older'
10 >>> 

空函數:

  什么叫做”空函數“?所謂的空函數就是你定義一個函數,但是什么也不用做,就是定義了一個空函數!例如:

1 >>> def Empty():
2 ...     pass
3 ... 
4 >>> def Empty2():
5 ...     pass
6 ... 
7 >>> 

pass語句什么都不做,那有什么用?實際上pass可以用來作為占位符,比如現在還沒想好怎么寫函數的代碼,就可以先放一個pass,讓代碼能運行起來。

pass還可以用在其他語句里,比如:

 1 >>> age = 90
 2 >>> if age:
 3 ...     pass
 4 ... 
 5 >>> #如果沒有pass就會報錯。
 6 ... 
 7 >>> if age:
 8 ... 
 9   File "<stdin>", line 2
10     
11     ^
12 IndentationError: expected an indented block
13 >>> 

調用函數時,如果參數個數不對,Python解釋器會自動檢查出來,並拋出TypeError

但是如果參數類型不對,Python解釋器就無法幫我們檢查。

當傳入了不恰當的參數時,內置函數abs會檢查出參數錯誤,而我們定義沒有參數檢查,所以,這個函數定義不夠完善。

以下定義一個求絕對值的函數:

 1 >>> def get_abs(value):
 2 ...     if value < 0:
 3 ...         return -value
 4 ...     else:
 5 ...         return value
 6 
 7 >>> get_abs(2)
 8 2
 9 >>> get_abs(-3)
10 3
11 >>> get_abs(0)
12 0
13 >>> 

讓我們修改一下我們函數定義get_abs,對參數類型做檢查,只允許整數和浮點數類型的參數。數據類型檢查可以用內置函數isinstance實現:

 

 1 >>> get_abs(dada)
 2 Traceback (most recent call last):
 3   File "<stdin>", line 1, in <module>
 4 NameError: name 'dada' is not defined
 5 >>> #在沒有進行參數檢查之前,報出了一個NameError:的錯誤
 6 ... #以下我們對函數進行參數檢查,提高函數的健壯性。
 7 ... 
 8 >>> def get_abc(value):
 9 ...     if not isinstance(value,(int,float)):
10 ...         raise TypeError('參數類型不對,不好意思啊!')
11 ...     if value >= 0:        
12 ...         return value
13 ...     else:
14 ...         return -value
15 ... 
16 >>> #傳入正確的參數:
17 ... 
18 >>> get_abc(12)
19 12
20 >>> get_abc(-12)
21 12
22 >>> #傳入錯誤的參數:
23 >>> get_abc('gn')
24 Traceback (most recent call last):
25   File "<stdin>", line 1, in <module>
26   File "<stdin>", line 3, in get_abc
27 TypeError: 參數類型不對,不好意思啊!
28 >>> 

 

返回多個值:

比如在游戲中經常需要從一個點移動到另一個點,給出坐標、位移和角度,就可以計算出新的新的坐標:

 

1 import math
2 
3 def move(x, y, step, angle=0):
4     nx = x + step * math.cos(angle)
5     ny = y - step * math.sin(angle)
6     return nx, ny

例子:

 1 >>> import math
 2 >>> 
 3 >>> def move(x,y,step,angle = 0):
 4 ...     nx = x + step*math.cos(angle)
 5 ...     ny = y - step*math.sin(angle)
 6 ...     return nx,ny
 7 ... 
 8 >>> x1,y1 = move(100,10,60,math.pi/6) 
 9 >>> print x1,y1
10 151.961524227 -20.0
11 >>> 

其實,返回的值是一個元組(tuple)。

 

函數的參數:

定義函數的時候,我們把參數的名字和位置確定下來,函數的接口定義就完成了。對於函數的調用者來說,只需要知道如何傳遞正確的參數,以及函數將返回什么樣的值就夠了,函數內部的復雜邏輯被封裝起來,調用者無需了解。

Python的函數定義非常簡單,但靈活度卻非常大。除了正常定義的必選參數外,還可以使用默認參數、可變參數和關鍵字參數,使得函數定義出來的接口,不但能處理復雜的參數,還可以簡化調用者的代碼。

1.默認參數:

定義一個計算平方的函數:power()

1 >>> def power(x):
2 ...     return x*x
3 ... 
4 >>> power(5)
5 25
6 >>> power(7)
7 49
8 >>> 

當我們調用power函數時,必須傳入有且僅有的一個參數x.但是此時我們要定義一個計算x的三次方甚至更多的次方該怎么辦呢,或許聰明的讀者已經想到了把power(x)修改為:power(x,n)實例如下:

 1 >>> def power(x,n):
 2 ...     s = 1
 3 ...     while n > 0:
 4 ...         n = n-1
 5 ...         s = s * x
 6 ...     print s
 7 ... 
 8 >>> power(2,2)
 9 4
10 >>> power(2,9)
11 512
12 >>> #但是此時想要使用power(x)求x的平方的話,該方法已經沒有效果了。
13 ... 
14 >>> power(5)
15 Traceback (most recent call last):
16   File "<stdin>", line 1, in <module>
17 TypeError: power() takes exactly 2 arguments (1 given)
18 >>> #此處我們可以引入默認參數來解決這個問題:
19 ... 
20 >>> def power(x,n = 2):
21 ...     s = 1
22 ...     while n > 0:
23 ...         n -= 1
24 ...         s *= x
25 ...     print s
26 ... 
27 >>> power(5,2)
28 25
29 >>> power(5)
30 25
31 >>> #這樣就可以實現效果了!是不是很帥!

而對於n > 2的其他情況,就必須明確地傳入n,比如power(5, 3)

從上面的例子可以看出,默認參數可以簡化函數的調用。設置默認參數時,有幾點要注意:

一是必選參數在前,默認參數在后,否則Python的解釋器會報錯(思考一下為什么默認參數不能放在必選參數前面);

二是如何設置默認參數。

當函數有多個參數時,把變化大的參數放前面,變化小的參數放后面。變化小的參數就可以作為默認參數。

使用默認參數有什么好處?最大的好處是能降低調用函數的難度。

可見,默認參數降低了函數調用的難度,而一旦需要更復雜的調用時,又可以傳遞更多的參數來實現。無論是簡單調用還是復雜調用,函數只需要定義一個。

默認參數雖然可以帶來很大的便利,但是也有一個相當大的弊端:

先定義一個函數,傳入一個list,添加一個END再返回:

 1 >>> def add_end(L=[]):
 2 ...     L.append(['END'])
 3 ...     return L
 4 ... 
 5 >>> add_end([1,2,3])
 6 [1, 2, 3, ['END']]
 7 >>> add_end(['x','y','z'])
 8 ['x', 'y', 'z', ['END']]
 9 >>> #當你使用默認參數的時候,剛開始是對的:
10 ... 
11 >>> add_end()
12 [['END']]
13 >>> #但是再次調用add_end()時,結果就不對了
14 ... 
15 >>> add_end()
16 [['END'], ['END']]
17 >>> add_end()
18 [['END'], ['END'], ['END']]
19 >>> 

默認參數是[],但是函數似乎每次都“記住了”上次添加了'END'后的list。這是為什么呢?

Python函數在定義的時候,默認參數L的值就被計算出來了,即[],因為默認參數L也是一個變量,它指向對象[],每次調用該函數,如果改變了L的內容,則下次調用時,默認參數的內容就變了,不再是函數定義時的[]了。

所以,定義默認參數要牢記一點:默認參數必須指向不變對象!

要修改上面的例子,我們可以用None這個不變對象來實現:

 1 >>> def add_end(L = None):
 2 ...     if L is None:
 3 ...         L = []
 4 ...     L.append('END')
 5 ...     return L
 6 ... 
 7 >>> #演示:
 8 ... 
 9 >>> add_end()
10 ['END']
11 >>> add_end()
12 ['END']
13 >>> add_end()
14 ['END']
15 >>> 

為什么要設計str、None這樣的不變對象呢?因為不變對象一旦創建,對象內部的數據就不能修改,這樣就減少了由於修改數據導致的錯誤。此外,由於對象 不變,多任務環境下同時讀取對象不需要加鎖,同時讀一點問題都沒有。我們在編寫程序時,如果可以設計一個不變對象,那就盡量設計成不變對象。

 

2. 可變參數:

在Python函數中可以傳入默認參數,但是也可以傳入可變參數.不難理解,可變參數的含義就是說,參數的個數時可以改變的,可以是1個、2個到任意個,還可以是0個。

我們以數學題為例子,給定一組數字a,b,c……,請計算a2 + b2 + c2 + ……。

要定義出這個函數,我們必須確定輸入的參數。由於參數個數不確定,我們首先想到可以把a,b,c……作為一個list或tuple傳進來,這樣,函數可以定義如下:

在沒有使用可變參數的情況:

 1 >>> def get_value(numbers):
 2 ...    sum = 0
 3 ...    for n in numbers:    
 4 ...        sum += n * n
 5 ...    return sum
 6 ... 
 7 >>> #但是調用的時候要先拼接一個元組或者列表
 8 ... 
 9 >>> get_value([1,2,3,4,5,6,7,8,9,10])
10 385
11 >>> get_value((1,2,3,3,4,5,6))
12 100

使用可變參數,把函數修改為如下:

1 >>> def get_value(*numbers):
2 ...     sum = 0
3 ...     for n in numbers:
4 ...         sum += n*n
5 ...     return sum
6 ... 
7 >>> get_value(1,2,3,4,5,6)
8 91

定義可變參數和定義list或tuple參數相比,僅僅在參數前面加了一個*號。在函數內部,參數numbers接收到的是一個tuple,因此,函數代碼完全不變。但是,調用該函數時,可以傳入任意個參數,包括0個參數:

 1 >>> get_value(1) 2 1 3 >>> get_value(0) 4 0 

但是,當你傳入一個列表或者元組會出現錯誤:

 1 >>> get_value([1,2,3,4,5,6])
 2 Traceback (most recent call last):
 3   File "<stdin>", line 1, in <module>
 4   File "<stdin>", line 4, in get_value
 5 TypeError: can't multiply sequence by non-int of type 'list'
 6 >>> get_value((1,2,3,4,5,6))
 7 Traceback (most recent call last):
 8   File "<stdin>", line 1, in <module>
 9   File "<stdin>", line 4, in get_value
10 TypeError: can't multiply sequence by non-int of type 'tuple'
11 >>> 

可以這樣解決列表和元組的問題:

 1 >>> list1 = [8,9]
 2 >>> tuple1 = (1,2,3)
 3 >>> list1
 4 [8, 9]
 5 >>> tuple1
 6 (1, 2, 3)
 7 >>> get_value(list1[0],list1[1])
 8 145
 9 >>> get_value(tuple1[0],tuple1[1],tuple1[2])
10 14
11 >>> 

但是,這種方法不是最好的,假如元組或者列表很大的時候就會出現工作量的問題,因此有一個更好的辦法就是:Python允許你在list或tuple前面加一個*號,把list或tuple的元素變成可變參數傳進去:

1 >>> list1
2 [8, 9]
3 >>> tuple1
4 (1, 2, 3)
5 >>> get_value(*tuple1)
6 14
7 >>> get_value(*list1)
8 145
9 >>> 

有木有感覺python真的是無可挑剔啊,確實是這樣的。

 

3. 關鍵字參數:

可變參數允許你傳入0個或任意個參數,這些可變參數在函數調用時自動組裝為一個tuple。而關鍵字參數允許你傳入0個或任意個含參數名的參數,這些關鍵字參數在函數內部自動組裝為一個dict。請看示例:

1 >>> def person(name,age,**kw):
2 ...     print 'name:',name,'age:',age,'other:',kw
3 ... 
4 >>> #函數person除了必選參數name和age外,還接受關鍵字參數kw。在調用該函數時,可以只傳入必選參數:
5 ... 
6 >>> person('lt',20)
7 name: lt age: 20 other: {}
8 >>> 

也可以傳入任意個關鍵字參數:

1 >>> person('lt',20,city = 'shanghai',height = 178,love = 'lt')
2 name: lt age: 20 other: {'city': 'shanghai', 'love': 'lt', 'height': 178}
3 >>> person('lt',20,city = 'shanghai',height = 178,love = 'lt',salary = 200000)
4 name: lt age: 20 other: {'salary': 200000, 'city': 'shanghai', 'love': 'lt', 'height': 178}
5 >>> 

關鍵字參數有什么用?它可以擴展函數的功能。比如,在person函數里,我們保證能接收到nameage這兩個參數,但是,如果調用者願意提供更多的參數,我們也能收到。試想你正在做一個用戶注冊的功能,除了用戶名和年齡是必填項外,其他都是可選項,利用關鍵字參數來定義這個函數就能滿足注冊的需求。

和可變參數類似,也可以先組裝出一個dict,然后,把該dict轉換為關鍵字參數傳進去:

1 >>> kw = {'name':'lt','city':'shanghai','hometown':'xinnong','age':20}
2 >>> kw
3 {'city': 'shanghai', 'age': 20, 'name': 'lt', 'hometown': 'xinnong'}
4 >>> person('jack',20,city=kw['city'],hometown = kw['hometown'])
5 name: jack age: 20 other: {'city': 'shanghai', 'hometown': 'xinnong'}

但是,更簡單便捷的方法時這樣的:

1 >>> kw = {'city':'shanghai','hometown':'xingnong'}
2 >>> kw
3 {'city': 'shanghai', 'hometown': 'xingnong'}
4 >>> person('Jack',12,**kw)
5 name: Jack age: 12 other: {'city': 'shanghai', 'hometown': 'xingnong'}
6 >>> 

4.參數組合:

在Python中定義函數,可以用必選參數、默認參數、可變參數和關鍵字參數,這4種參數都可以一起使用,或者只用其中某些,但是請注意,參數定義的順序必須是:必選參數、默認參數、可變參數和關鍵字參數。

比如定義一個函數,包含上述4種參數:

1 >>> def func(a,b,c=0,*args,**kw):
2 ...     print 'a=',a,'b=',b,'c=',c,'args = ',args,'kw = ',kw
3 ... 
4 >>> func(1,2)
5 a= 1 b= 2 c= 0 args =  () kw =  {}
1 >>> func(1,2,c=3)
2 a= 1 b= 2 c= 3 args =  () kw =  {}
3 >>> func(1,2,3,'a','b')
4 a= 1 b= 2 c= 3 args =  ('a', 'b') kw =  {}
5 >>> func(1,2,3,'a','b',x = 45,y = 65,z = 32)
6 a= 1 b= 2 c= 3 args =  ('a', 'b') kw =  {'y': 65, 'x': 45, 'z': 32}
7 >>> 

最神奇的是通過一個tuple和dict,你也可以調用該函數:

1 >>> args = (1, 2, 3, 4)
2 >>> kw = {'x': 99}
3 >>> func(*args, **kw)
4 a = 1 b = 2 c = 3 args = (4,) kw = {'x': 99}

所以,對於任意函數,都可以通過類似func(*args, **kw)的形式調用它,無論它的參數是如何定義的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM