一段程式碼可能要在多處使用,就可以把這段程式碼定義為函式,需要的時候直接呼叫就可以——抽象
fibs = [0, 1]
for i in range(8):
fibs.append(fib[-2] + fibs[-1])
>>> fibs
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
抽象一點:
num = raw_input('How many numbers do you wants? '
print fibs(num) #呼叫自定義的fibs函式,抽象了程式碼。需要時直接呼叫就OK
6.2 抽象與結構
程式應該是非常抽象的,就像“下載網頁、計算頻率,列印每個單詞的頻率”一樣易懂:
page = download_page()
freps = compute_frequencies(page)
for word, frep in freps:
print word, frep
#讀完就知道程式做什麼了,具體細節會在其他地方寫出——在獨立的函式定義中
6.3 建立函式
函式可以呼叫:它執行某種行為並返回一個值;一般內建的callable函式可以用來判斷函式是否可呼叫:
>>> import math
>>> x = 1
>>> y = math.sqrt
>>> callable(x)
False
>>> callable(y)
True
使用def語句即可定義函式
def hello(name):
return 'Hello, ' + name + '!'
此時就可以呼叫hello函式:
>>>print hello('world')
Hello, world!
>>> print hello('Gumby')
Hello, Gumby!
#定義斐波那契函式
def fibs(num):
result = [0, 1]
for i in range(num-2):
result.append(result[-2] + result[-1])
return result
#此時直接呼叫函式就可求斐波那契序列
>>> fibs(10)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
6.3.1 記錄函式
想給函式寫文件,讓後面使用函式的人能理解,可以加註釋(以#號開頭)
還可以直接寫上字串,在函式的開頭寫下字串,會作為函式的一部分進行儲存——稱為文件字串:
def square(x):
'Calculates the square of the number x'
return x*x
#文件字串可以按如下方式訪問:
>>> square._doc_
'Calculates the square of the number x'
#內建的help函式非常有用。在互動式直譯器中使用它,就可得到關於函式,包括他的文件字串的資訊:
>>> help(square)
help on function square in module_main_:
square(x)
Calxulates the square of the number x
6.3.2 並非真正函式的函式
有的函式並不會返回值,不同於學術意義上的函式
def test():
print 'This is printed'
return
print 'This is not'
>>> x = test()
This is printed
>>> x
>>>
>>> print x
None
def add(x, y):
return x + y
>>> params = (1,2)
>>> add(*params) *不是收集引數,而是分配它們到另一端——*在呼叫函式時使用而不是定義時使用
3
def hello_3(greeting='Hello',name='world')
print '%s, %s' % (greeting,name)
params = {'greeting': 'Well met', 'name': 'Sir Robin'}
>>> hello_3(**params)
Well met, Sir Robin
#在定義和呼叫中都使用*或**,與在定義和呼叫中都不用*和**得到同樣的效果。所以*或**只有在定義或呼叫單獨使用時才有用
def withstars(**kwds):
print kwd['name'], 'is', kwd['age'], 'years old'
def withoutstars(kwds):
print kwd['name'], 'is', kwd['age'], 'years old'
>>> args = {'names': 'Mr Gumby', 'age': 42}
>>> withstars(**args)
Mr Gumby is 42 years old
>>> withoutstars(args)
Mr Gumby is 42 years old
使用拼接(Splicing)操作符“傳遞”引數很有用,因為不用關心引數的個數之類的問題:
def foo(x, y, z, m=0, n=0):
print x, y, z, m, n
def call_foo(*arg, **kwds):
print 'Calling foo!'
foo(*arg, **kwds)
6.4.6 練習使用引數
def story(**kwds):
return 'Once upon a time, there was a ' \
'%(job)s called %(name)s.' % kwds
def power(x, y, *others):
if others:
print 'Received redundant parameters:', others
return pow(x, y)
def iterval(start, stop=None, step=1):
'Imitates range() for step > 0'
if stop is None:
start, stop = 0, start
result = []
i = start
while i < stop:
result.append(i)
i += step
return result
>>> print story(job ='king', name='Gumby')
Once upon a time, there was a king called Gumby.
>>> print story(name='Sir Robin', job='brave knight')
Once upon a time, there was a brave knight called Sir Robin.
>>> params = {'job':'language', 'name':'Python'}
>>> story(**params)
Once upon a time, there was a language called Python
>>> del params['job']
>>> print story(job='stroke of genius', **params)
Once upon a time, there was a stroke of genius called Python.
>>> power(2, 3)
8
>>> power(3, 2)
9
>>> power(y=3, x=2)
8
>>> params=(5,)*2
>>> power(*params)
3125
>>> power(3, 3, 'Hello, world)
Received redundant parameters: ('Hello, world',)
27
>>> iterval(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> iterval(1, 5)
[1, 2, 3, 4]
>>> iterval(3, 12, 4)
[3, 7, 11]
>>> power(*iterval(3,7))
Received redundant parameters:(5, 6)
81
6.5 作用域
x = 1 變數和所對應的值用的是個不可見的字典:x引用1.
這類不可見字典叫做名稱空間或作用域,每個函式呼叫都會建立一個新的作用域:
def foo(): x=42
>>> x = 1
>>> foo()
>>> x
1
最終x的值還是1,因為呼叫函式foo時,新的名稱空間被建立,它作用於foo內的程式碼塊,並不影響全域性中的x。函式內的變數被稱為區域性變數,所以用全域性變數名做區域性變數名沒有問題。
def output(x): print x
>>> x= 1
>>> y = 2
>>> output(y)
2
需要在函式內訪問全域性變數怎麼辦?如果只想讀取變數的值(不想重新繫結變數),一般沒問題。
>>> def combine(parameter): print parameter + external
>>> external = 'berry'
>>> combine('Shrub')
Shrubberry
但是這樣引用全域性變數容易引發很多錯誤,所以還要慎重使用。
遮蔽的問題:如果區域性變數或引數的名字和想訪問的全域性變數名相同,全域性變數會被區域性變數遮蔽,從而不能訪問。此時可使用globals函式獲取全域性變數值——返回全域性變數的字典
def combine(parameter):
print parameter + globals()['parameter']
>>> parameter = 'berry'
>>> combine('Shrub')
Shrubberry
重新繫結全域性變數——向Python宣告其為全域性變數,否則其自動成為區域性變數:
>>> x = 1
def change_global():
global x
x = x+1
>>> change_global()
>>> x
2
巢狀作用域——講一個函式放入另一個裡面
def multiplier(factor):
def multiplyByFactor(number):
return number*factor
return multiplyByFactor
>>> double = multiplier(2)
>>> double(5)
10
>>> multiplier(5)(4)
20
6.6 遞迴——函式可呼叫自身
6.6.1 兩個經典:階乘和冪
階乘,用迴圈來編寫:
def factorial(n):
result = n
for i in range(1,n):
result *= i
return result
遞迴的版本:1的階乘是1大於1的階乘是n*(n-1)的階乘
def iterfactorial(n):
if n == 1:
return 1
else:
return n*iterfactorial(n-1)
計算冪,pow函式或**運算子:數自乘n-1次的結果
改為迴圈的編寫:
def power(x, n):
result = 1
for i in range(n):
result *= x
return result
改為遞迴版本:
對任意數字x,power(x,0) =1
對任意大於0的數來說,power(x,n)=x*power(x,n-1)
def iterpower(x, n):
if n == 0:
return 1
else:
return x*iterpower(x,n-1)