要呼叫一個函數,需要知道函數的名稱和參數,例如求絕對值的函數 abs,它接收一個參數。
可以直接從Python的官方網站查看文件:
http://docs.python.org/2/library/functions.html#abs
也可以在交互式命令列透過 help(abs) 查看abs函數的幫助資訊。
呼叫 abs 函數:
>>> abs(100)
100
>>> abs(-20)
20
>>> abs(12.34)
參數數量不對,會報TypeError的錯誤,並且Python會明確地告訴你:abs()有且僅有1個參數,但給了兩個:
Traceback ( most recent call last):
File "TypeError: abs() takes exactly one argument (2 given)
如果傳入的參數數量是對的,但有參數類型不能被函數所接受,也會回報TypeError的錯誤,並且給出錯誤訊息:str是錯誤的參數類型:
Traceback (most recent call last):
File "TypeError: bad operand type for abs(): 'str'
而比較函數 cmp(x, y) 就需要兩個參數,如果 x
-1
>>> cmp(2, 1)1
>>>
>>> cmp(2, 1)
1
>>> cmp(3, 3)
0
Python內建的常用函數也包含資料型別轉換函數,如 int()函數可以將其他資料型別轉換為整數:
>>> int('123')
123
>>> int(12.34)
12
str()函數把其他型別轉換成str:
>>> str(123)
'123'
>>> str(1.23)
'123'
>>> str(1.23)
def my_abs(x):
if x >= 0:
return x turn x 注意,函數體內部的語句在執行時,一旦執行到return時,函數就會執行完畢,並將結果傳回。因此,函數內部透過條件判斷和循環可以實現非常複雜的邏輯。
def move(x, y, step, angle):
nx = x + step * math.cos(angle) ny = y - step * math.sin(angle)
return nx, ny
這樣我們就可以同時得到回傳值:
>>> print x, y
151.961520296170.
但其實這只是一種假象,Python函數回傳的還是單一值:
>>> print r
(15196163270661 , 70.0)
用print列印回傳結果,原來回傳值是個tuple!
if n== 1:
return 1 return n * fact(n - 1)
使用遞歸函數需要注意防止棧溢位。在電腦中,函數調用是透過棧(stack)這種資料結構實現的,每當進入函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。由於棧的大小不是無限的,所以,遞歸呼叫的次數過多,會導致棧溢位。可以試試計算 fact(10000)。
函數 move(n, a, b, c) 的定義是將 n 個圓盤從 a 藉由 b 移動到 c。
參考代碼:
def move(n, a, b, c):
if n ==1:
print a, '-->', c
print a, '-->', c
print a, '-->', c
b)
print a, '-->', c
move(n-1, b, a, c)
move(4, 'A', 'B', 'C')
def fact(n):
return fact_iter(n, 1)def fact_iter(num, product) :
if num == 1: return product return fact_iter(num - 1, num * product)
,
num可以看到,return fact_itcter(dact) * product在函數呼叫前就會被計算,不影響函數呼叫。 fact(5)對應的fact_iter(5, 1)的呼叫如下:
===> fact_iter(5, 1)
===> fact_iter(4, 5)
===> fact_iter(3, 20)
===> fact_iter(2, 60)
===> fact_iter(1, 120)
===> 120
>>> int('123')
123
>>> int('123', 8)
83
def power(x, n):
s = 1
while n > 0:
return s
s = 1
while n > 0: 1
s = s * x
return s
這樣一來,計算平方就不需要傳入兩個參數了:
>>> power(5)
25到右邊的順序匹配,所以預設參數只能定義在必需參數的後面:
# OK:def fn1(a, b=1, c=2):
pass# Error:def fn2(a=1, b ):
先定義一個函數,傳入一個list,加上一個END再回:
def add_end(L=[]):
L.append('END') 正常呼叫時,結果似乎不錯:
>>> add_end([1, 2, 3])[1, 2, 3, 'END']>>> add_end(['x', 'y', 'z '])['x', 'y', 'z', 'END']
當你使用預設參數呼叫時,一開始結果也是對的:
但是,再次呼叫add_end()時,結果就不對了:
很多初學者都很疑惑,預設參數是[],但是函數似乎每次都「記住了」上次添加了'END'後的list。
原因解釋如下:
Python函數在定義的時候,預設參數L的值就被計算出來了,即[],因為預設參數L也是一個變量,它指向物件[],每次呼叫該函數,如果改變了L的內容,則下次呼叫時,預設參數的內容就變了,不再是函數定義時的[]了。
所以,定義預設參數要牢記一點:預設參數必須指向不變物件!
要修改上面的例子,我們可以用None這個不變物件來實現:
要修改上面的例子,我們可以用None這個不變物件來實現:
def add_end(L=None):
if L is None:
L = []
現在,無論呼叫多少次,都不會有問題:
為什麼要設計str、None這樣的不變對象呢?因為不變物件一旦創建,物件內部的資料就不能修改,這樣就減少了因為修改資料而導致的錯誤。此外,由於物件不變,多任務環境下同時讀取物件不需要加鎖,同時讀一點問題都沒有。我們在寫程式時,如果可以設計一個不變對象,那就盡量設計成不變對象。
定義可變參數:
如果想要讓函數能接受任一參數,我們可以定義一個變化參數:
def fn(*args):print參數的名字前面有個 * 號,我們可以傳入0個、1個或多個參數給可變參數:
>>> fn()
()>>>> fn('a')
( 'a',)
>>> fn('a', 'b')
('a', 'b')
>>> fn('a', 'b', 'c')
(' a', 'b', 'c')
Python解釋器會把傳入的一組參數組裝成一個tuple傳遞給可變參數,因此,在函數內部,直接把變數 args 看成一個tuple 就好了。
關鍵字參數:
可變參數允許你傳入0個或任意個參數,這些可變參數在函數呼叫時自動組裝為一個tuple。而關鍵字參數允許你傳入0個或任一包含參數名的參數,這些關鍵字參數在函數內部會自動組裝為一個dict。請看範例:
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)
person除了必選函數參數name和age外,也接受關鍵字參數kw。呼叫函數時,可以只傳入必選參數:
>>> person('Michael', 30)
name: Michael age: 30 other: {}
也可以傳入任意個數的關鍵字元參數:
>>> person('Bob', 35, city='Beijing')
name: Bob age: 35 other: {'city': 'Beijing'}>>> person('Adam', 45 , gender='M', job='Engineer')name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
關鍵字參數有什麼用?它可以擴展函數的功能。例如,在person函數裡,我們保證能接收到name和age這兩個參數,但是,如果呼叫者願意提供更多的參數,我們也能收到。試想你正在做一個用戶註冊的功能,除了用戶名和年齡是必填項外,其他都是可選項,利用關鍵字參數來定義這個函數就能滿足註冊的需求。
命名關鍵字參數:
對於關鍵字參數,函數的呼叫者可以傳入任意不受限制的關鍵字參數。至於到底傳入了哪些,就需要在函數內部通過kw檢查。
仍以person()函數為例,我們希望檢查是否有city和job參數:
def person(name, age, **kw):
if 'city' in kw: if 'job' in kw: # 有job參數 pass
print('name:', name, 'age:', age, 'other:', kw)
的關鍵字參數:
>>> person('Jack', 24, city='Beijing', addr='Chaoyang', zipcode=123456)
def person(name, age, *, city, job):
和關鍵字參數**kw不同,命名關鍵字參數**kw不同,命名關鍵字參數需要一個特殊分隔符號*,*後面的參數被視為命名關鍵字參數。
呼叫方式如下:
>>> person('Jack', 24, city='Beijing', job='Engineer')
Jack 24 Beijing Engineer
命名參數必須傳入參數名,這和位置參數不同。如果沒有傳入參數名,呼叫會錯誤:
>>> person('Jack', 24, 'Beijing', 'Engineer')
Traceback (most recent call last):
File "
由於呼叫時缺少參數名稱city和job,Python解釋器把這4個參數都視為位置參數,但person()函數僅接受2個位置參數。
命名關鍵字參數可以有缺省值,從而簡化呼叫:
def person(name, age, *, city='Beijing', job):
print(name, age, city, job)
Jack 24 Beijing Engineer
使用命名關鍵字參數時,要特別注意,*不是參數,而是特殊分隔符號。若缺少*,Python解釋器將無法辨識位置參數和命名關鍵字參數:
def person(name, age, city, job):
# 缺少*,city和job被視為位置參數
參數組合:
在Python中定義函數,可以用必選參數、預設參數、可變參數、關鍵字參數和命名關鍵字參數,這5種參數都可以組合使用,除了可變參數無法和命名關鍵字參數混合。但請注意,參數定義的順序必須是:必選參數、預設參數、可變參數/命名關鍵字參數和關鍵字參數。
例如定義一個函數,包含上述若干種參數:
print('a =', a, 'b =', b , 'c =', c, 'args =', args, 'kw =', kw)def f2(a, b, c=0, *, d, **kw):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)在函數呼叫的時候,Python解釋器自動依照參數位置和參數名把對應的參數傳進去。
>>> f1(1, 2)
a = 1 b = 2 c = 0 args = () kw = {}>>> f1(1, 2, c=3)a = 1 b = 2 c = 3 args = () kw = {}>>> f1(1, 2, 3, 'a', 'b')
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}>>> f1(1, 2, 3, 'a', 'b', x=99)a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}>>> f2(1, 2, d=99, ext=None)
a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}最神奇的是透過一個tuple和dict,你也可以呼叫上述函數:
>>> args = (1, 2, 3, 4)>>> kw = {'d': 99, 'x': '# '}>>> f1(*args, **kw)
a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}>>> args = (1, 2, 3)>>> kw = {'d': 88, 'x': '#'}>>> f2(*args, **kw)a = 1 b = 2 c = 3 d = 88 kw = {'x': '#'}
所以,對於任意函數,都可以透過類似func(*args, **kw)的形式呼叫它,無論它的參數是如何定義的。 小結Python的函數具有非常靈活的參數形態,既可以實現簡單的調用,又可以傳入非常複雜的參數。
預設參數一定要用不可變對象,如果是可變對象,程式運作時會有邏輯錯誤!