先寫一個例子——產生n以內的Fibonacci數:
def fib(n):
a,b=0,1
while b<n:
print b,
a,b=b,a+b
def指出了接下來的部分是一個函數。Python和C語言類似,並不區分過程(無傳回值)和函數(有傳回值)。像上面那樣嚴格的說就是一個過程(Procedure)。
對於一個項目而言,在不同的模組的代碼中插入一段簡潔明了的解說文字是很有必要的。Python支援類似Java-Doc的文法。例如:
def fib(n):
"""Print a Fibonacci series up to n."""
a,b=0,1
while b<n:
print b,
a,b=b,a+b
將解說文字放在一對“”“(三個又引號)之間,一些文檔產生工具就會將這些文字提取出來,產生HTML或是LaTeX等文檔。寫這些文字費不了多少時間,養成這種習慣絕對是大有好處的。
函數的調用也非常簡單,對於上面的而言,只需直接用fib(100),就會產生相應的輸出:1 1 2 3 5 8 13 21 34 55 89
函數本身也可以被引用。對於已經定義好的fib來說,如果有一句:f=fib,就意味著f引用了fib這個函數。現在這兩個名字所指的就是同一個東西。於是就可以這樣使用:>>>f(100)
輸出結果和上面的是一樣的。可以比照著C語言中的”函數指標“來理解一下。
雖然Python的函數可以不顯式地寫出return語句,實際上這種函數也會返回一個特殊的值None。例如有一個什麼都不乾的函數:
def fun():
pass
可以通過在解譯器中用print來顯示一下它的值:>>>print fun()
None
現在可以將上面的fib函數改造一下,讓它不再直接產生輸出,而是產生一個Fibonacci數列的List:def fib2(n):
"""Print a Finonacci series up to n."""
a,b=0,1
result=[]
while b<n:
result.append(b)
a,b=b,a+b
return result
append是List類的一個方法,即向一個List後添加元素。這個函數的效果如下:>>>print fib2(100)
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
預設參數
Python函數支援預設參數。你可以定義一個兩個參數的函數,但在調用的時候可以只告訴其中一個參數或是一個都不告訴——只要你在定義的時候指定了參數的預設值。這很像是從C++中學來的。
def ask_ok(prompt,retries=4,complaint='Yes or no, please!'):
while True:
ok=raw_input(prompt)
if ok in ('y','ye','yes'): return True
if ok in ('n','no','nop','nope'): return False
retries=retries-1
if retries<0: raise IOError, 'refusenik user'
print complaint
這樣就有了多種調用方式:>>>ask_ok('Do you really want to quit?')
>>>ask_ok('Do you really want to quit?',2)
>>>ask_ok('Do you really want to quilt?',3,'What do you want to do?')
參數的預設值是在定義的時在函數的入口處確定下來的。這可以通過一個實驗來說明:i = 5
def f(arg=i):
print arg
i = 6
f()
發現輸出的是5而不是6,因為後來的i改變根本不會對函數裡的arg造成影響。
要注意的是,函數的預設參數部分只會被計算一次。這樣如果預設參數是某種複合類型(List,String,Class),可能會產生一種”積累效應“:
def f(a,L=[]):
L.append(a)
return L
如果調用三次:>>> print f(1)
[1]
>>> print f(2)
[1, 2]
>>> print f(3)
[1, 2, 3]
要避免這種情況,就需要在定義的參數列表中顯式地將L的預設值指定為None:
def f(a,L=None):
if L is None:
L=[]
L.append(a)
return L
帶關鍵字的參數
關鍵字可以理解為函數頭部參數的名字。通過在調用函數的時候指定關鍵字,可以不按照參數定義的順序來傳遞參數的值。def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
print "-- This parrot wouldn't", action,
print "if you put", voltage, "volts through it."
print "-- Lovely plumage, the", type
print "-- It's", state, "!"
帶關鍵字調用:parrot(1000)
parrot(action = 'VOOOOOM', voltage = 1000000)
parrot('a thousand', state = 'pushing up the daisies')
parrot('a million', 'bereft of life', 'jump')
下面的幾種調用方式是錯的:parrot() # 缺少指定參數
parrot(voltage=5.0, 'dead') # 沒有指出關鍵字
parrot(110, voltage=220) # 傳遞重複參數
parrot(actor='John Cleese') # 不存在的關鍵字
更為靈活的是,可以通過在函數定義時用*name或**name的方式代表不確定的參數或帶有關鍵字的參數(Dictionary)。這個用例子說明會容易理解一下:
def cheeseshop(kind, *arguments, **keywords):
print "-- Do you have any", kind, '?'
print "-- I'm sorry, we're all out of", kind
for arg in arguments: print arg
print '-'*40
keys = keywords.keys()
keys.sort()
for kw in keys: print kw, ':', keywords[kw]
可以這樣調用:cheeseshop('Limburger', "It's very runny, sir.","It's really very, VERY runny, sir.",client='John Cleese', shopkeeper='Michael Palin',sketch='Cheese Shop Sketch')
結果是:-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
client : John Cleese
shopkeeper : Michael Palin
sketch : Cheese Shop Sketch
傳遞多個參數時還可以通過List來進行。List中的元素會被分離開,稱為Unpack,這是Python解譯器在幕後完成的。例如range函數可以指定兩個參數,可以把這兩個參數構成一個List,再將這個List傳入:>>> args = [3, 6]
>>> range(*args) # call with arguments unpacked from a list
[3, 4, 5]
類似地,可以使用**操作符來傳遞一個字典,對應於帶關鍵字傳遞參數的方式。>>> def parrot(voltage, state='a stiff', action='voom'):
... print "-- This parrot wouldn't", action,
... print "if you put", voltage, "volts through it.",
... print "E's", state, "!"
...
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !
用心領悟一下。
Lambda函數
Python從函數語言中學來了這一功能。Lambda函數是一種匿名函數,功能有些像函數的模板,通過它可能方便地產生其他函數。感覺Lambda函數更加需要領悟,只裡僅僅記幾個例子:>>> def make_incrementor(n):
... return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43