Python(六)語法 函式定義,函式引數
函式
Python內建了很多有用的函式,我們可以直接呼叫。
要呼叫一個函式,需要知道函式的名稱和引數,比如求絕對值的函式abs
,只有一個引數。可以直接從Python的官方網站檢視文件:
http://docs.python.org/3/library/functions.html#abs
也可以在互動式命令列通過help(abs)
檢視abs
函式的幫助資訊。
呼叫abs
函式:
>>> abs(100)
100
>>> abs(-20)
20
>>> abs(12.34)
12.34
呼叫函式的時候,如果傳入的引數數量不對,會報TypeError
abs()
有且僅有1個引數,但給出了兩個:
>>> abs(1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: abs() takes exactly one argument (2 given)
如果傳入的引數數量是對的,但引數型別不能被函式所接受,也會報TypeError
的錯誤,並且給出錯誤資訊:str
是錯誤的引數型別:
>>> abs('a') Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: bad operand type for abs(): 'str'
而max
函式max()
可以接收任意多個引數,並返回最大的那個:
>>> max(1, 2)
2
>>> max(2, 3, 1, -5)
3
資料型別轉換
Python內建的常用函式還包括資料型別轉換函式,比如int()
函式可以把其他資料型別轉換為整數:
>>> int('123') 123 >>> int(12.34) 12 >>> float('12.34') 12.34 >>> str(1.23) '1.23' >>> str(100) '100' >>> bool(1) True >>> bool('') False
函式名其實就是指向一個函式物件的引用,完全可以把函式名賦給一個變數,相當於給這個函式起了一個“別名”:
>>> a = abs # 變數a指向abs函式
>>> a(-1) # 所以也可以通過a呼叫abs函式
1
定義函式
在Python中,定義一個函式要使用def
語句,依次寫出函式名、括號、括號中的引數和冒號:
,然後,在縮排塊中編寫函式體,函式的返回值用return
語句返回。
在Python互動環境中定義函式時,注意Python會出現...
的提示。函式定義結束後需要按兩次回車重新回到>>>
提示符下
>>> def hello(name):
... return 'hello:'+name
...
>>> hello('xuxu')
hello:xuxu
>>>
函式體內部的語句在執行時,一旦執行到return
時,函式就執行完畢,並將結果返回。因此,函式內部通過條件判斷和迴圈可以實現非常複雜的邏輯。
如果沒有return
語句,函式執行完畢後也會返回結果,只是結果為None
。return None
可以簡寫為return
。
如果你已經把hello()
的函式定義儲存為fun.py
檔案了,那麼,可以在該檔案的當前目錄下啟動Python直譯器,用from fun import hello
來匯入hello()
函式,注意fun
是檔名(不含.py
副檔名):
>>> from fun import hello
>>> hello('xuxu')
hello:xuxu
>>>
空函式
如果想定義一個什麼事也不做的空函式,可以用pass
語句:
def nop():
pass
pass
語句什麼都不做,那有什麼用?實際上pass
可以用來作為佔位符,比如現在還沒想好怎麼寫函式的程式碼,就可以先放一個pass
,讓程式碼能執行起來。
pass
還可以用在其他語句裡,比如:
if age >= 18:
pass
缺少了pass
,程式碼執行就會有語法錯誤。
呼叫函式時,如果引數個數不對,Python直譯器會自動檢查出來,並丟擲TypeError:
>>> hello('x','x')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: hello() takes 1 positional argument but 2 were given
但是如果引數型別不對,Python直譯器就無法幫我們檢查,如下傳入數字型別明顯和之前定義的引數字串型別不一致,但是程式沒有報錯,知道最後返回時,執行到‘hello’+123這一步時才報錯:數值型別無法和字串型別相連
>>> hello(123)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in hello
TypeError: can only concatenate str (not "int") to str
對引數型別做檢查,只允許整數和浮點數型別的引數。資料型別檢查可以用內建函式isinstance()
實現
# -*- coding: utf-8 -*-
def hello(name):
if not isinstance(name,(str)):
raise TypeError('輸入引數不是字串')
return('hello:'+name)
>>> hello('1')
'hello:1'
>>> hello(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "E:\python\helloword\fun.py", line 4, in hello
raise TypeError('輸入引數不是字串')
TypeError: 輸入引數不是字串
>>>
返回多個值
函式可以返回多個值嗎?答案是肯定的。
def hello(x,y):
return x,y
>>> x,y=hello(1,2)
>>> print(x,y)
1 2
>>>
但其實這只是一種假象,Python函式返回的仍然是單一值
>>> r=hello(1,2)
>>> r
(1, 2)
原來返回值是一個tuple!但是,在語法上,返回一個tuple可以省略括號,而多個變數可以同時接收一個tuple,按位置賦給對應的值,所以,Python的函式返回多值其實就是返回一個tuple,但寫起來更方便。
一道數學題
請定義一個函式quadratic(a, b, c)
,接收3個引數,返回一元二次方程:
ax2 + bx + c = 0
的兩個解。
提示:計算平方根可以呼叫math.sqrt()
函式:
# -*- coding: utf-8 -*-
import math
def quadratic(a,b,c):
for x in (a,b,c):
if not isinstance(x,(int,float)):
raise TypeError('arg type error')
if a==0:
return -c/b
else:
data=b*b-4*a*c
if data==0:
return -b/(2*a)
elif data<0:
return '無解'
elif data>0:
return (math.sqrt(data)-b)/(2*a),(-math.sqrt(data)-b)/(2*a)
# 測試:
print('quadratic(2, 3, 1) =', quadratic(2, 3, 1))
print('quadratic(1, 3, -4) =', quadratic(1, 3, -4))
if quadratic(2, 3, 1) != (-0.5, -1.0):
print('測試失敗')
elif quadratic(1, 3, -4) != (1.0, -4.0):
print('測試失敗')
else:
print('測試成功')
函式的引數
Python的函式定義非常簡單,但靈活度卻非常大。除了正常定義的必選引數外,還可以使用預設引數、可變引數和關鍵字引數,使得函式定義出來的介面,不但能處理複雜的引數,還可以簡化呼叫者的程式碼
位置引數
我們先寫一個計算x2的函式:
>>> def power(x):
... return x*x
...
>>> power(5)
25
對於power(x)
函式,引數x
就是一個位置引數。
當我們呼叫power
函式時,必須傳入有且僅有的一個引數x
但是如果我們需要計算x的3次方時我們又得寫新的函式,以至於我們寫x的n次方我們得寫無數個函式
此時可以將幾次方N當做函式引數
>>> def power(x,n):
... return x**n
...
>>> power(2,3)
8
但是這樣一來如果使用者還想像以前一樣直接呼叫power(x)來算平方的時候就會因為缺少必須引數n報錯如下:
>>> power(2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: power() missing 1 required positional argument: 'n'
預設引數
這個時候,預設引數就排上用場了。由於我們經常計算x2,所以,完全可以把第二個引數n的預設值設定為2:
>>> def power(x,n=2):
... return x**n
...
>>> power(2)
4
>>> power(2,3)
8
從上面的例子可以看出,預設引數可以簡化函式的呼叫。設定預設引數時,有幾點要注意:
一是必選引數在前,預設引數在後,否則Python的直譯器會報錯(思考一下為什麼預設引數不能放在必選引數前面);
>>> def add(x,y=2,z):
... return x+y+z
...
File "<stdin>", line 1
SyntaxError: non-default argument follows default argument
因為如果使用者呼叫add(1,3) 那麼就會產生歧義,有可能是add(1,2,3),也有可能是add(1,2)
二是如何設定預設引數。
當函式有多個引數時,把變化大的引數放前面,變化小的引數放後面。變化小的引數就可以作為預設引數。
使用預設引數有什麼好處?最大的好處是能降低呼叫函式的難度。
多個預設引數時既可以按順序提供預設引數,也可以不按順序提供預設引數,但要帶上引數名
>>> def add(a,b,c=3,d=4):
... return a+b+c+d
...
>>> add(1,2)
10
>>> add(1,2,4)
11
>>> add(1,2,d=0)
6
add(1,2)相當於引數c和d全部採用預設值
add(1,2,4)相當於引數d採用預設值,也就是1+2+4+4
add(1,2,d=0)相當於指定引數d的值是0,c採用預設值 也就是1+2+3+0
定義預設引數要牢記一點:預設引數必須指向不變物件!舉個例子
>>> def add_end(L=[]):
... L.append('END')
... return L
...
>>> add_end()
['END']
>>> add_end()
['END', 'END']
>>> add_end()
['END', 'END', 'END']
發現如果將L預設引數指向list那麼每次執行預設方法時都是在後面累加END 而不是一個END
因為L指向的是一個物件,雖然物件一直沒變,但是物件在記憶體中的值改變了,只需要將L指向不變物件即可
>>> def add_end(L=None):
... if L==None:
... L=[]
... L.append('END')
... return L
...
>>> add_end()
['END']
>>> add_end()
['END']
可變引數
在Python函式中,還可以定義可變引數。顧名思義,可變引數就是傳入的引數個數是可變的,可以是1個、2個到任意個,還可以是0個
在引數前面加了一個*
號。在函式內部,引數nums
接收到的是一個tuple
>>> def add(*nums):
... sum=0
... for num in nums:
... sum=sum+num
... return sum
...
>>> add(1,2,3)
6
>>> add()
0
>>>
如果傳入的引數時一個list,Python允許你在list或tuple前面加一個*
號,把list或tuple的元素變成可變引數傳進去
>>> list=[1,2,3]
>>> add(*list)
6
>>>
關鍵字引數
可變引數允許你傳入0個或任意個引數,這些可變引數在函式呼叫時自動組裝為一個tuple。而關鍵字引數允許你傳入0個或任意個含引數名的引數,這些關鍵字引數在函式內部自動組裝為一個dict
>>> def person(name,age,**kw):
... print('name:',name,'age:',age,'other:',kw)
...
>>> person('xuxu',28,city='wuhan',sex='man')
name: xuxu age: 28 other: {'city': 'wuhan', 'sex': 'man'}
傳入dict
>>> dict={'city':'wuhan','sex':'man'}
>>> person('xuxu',28,**dict)
name: xuxu age: 28 other: {'city': 'wuhan', 'sex': 'man'}
命名關鍵字引數
要限制關鍵字引數的名字,就可以用命名關鍵字引數,和關鍵字引數**kw
不同,命名關鍵字引數需要一個特殊分隔符*
,*
後面的引數被視為命名關鍵字引數,命名關鍵字引數必須傳入引數名,這和位置引數不同。如果沒有傳入引數名,呼叫將報錯
>>> def person(name,*,age):
... print('name:',name,'age:',age)
...
>>> person('xuxu',age=28)
name: xuxu age: 28
>>> person('xuxu',28)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: person() takes 1 positional argument but 2 were given
如果函式定義中已經有了一個可變引數,後面跟著的命名關鍵字引數就不再需要一個特殊分隔符*
了
>>> def person(name,*args,age):
... print('name:',name,args,'age:',age)
...
>>> person('xuxu',age=28)
name: xuxu () age: 28
>>> person('xuxu',1,2,3,age=28)
name: xuxu (1, 2, 3) age: 28
>>>
引數組合
在Python中定義函式,可以用必選引數、預設引數、可變引數、關鍵字引數和命名關鍵字引數,這5種引數都可以組合使用。但是請注意,引數定義的順序必須是:必選引數、預設引數、可變引數、命名關鍵字引數和關鍵字引數。
比如定義一個函式,包含上述若干種引數:
>>> def args(a,b,c=3,*d,e,**kw):
... print('a:',a,'b:',b,'c:',c,'d:',d,'e:',e,'kw',kw)
...
>>> args(1,2,3,4,4,4,e=5,f=6,g=7)
a: 1 b: 2 c: 3 d: (4, 4, 4) e: 5 kw {'f': 6, 'g': 7}
>>>