6.1 懶惰即美德
假設我們編寫了一小段代碼來計算斐波那契數列:
fibs = [0,1]
for i in range(8):
fibs.append(fibs[-2] + fibs[-1])
fibs = [0,1]
num = input('How many Fibonacci numbers do you want?')
for i in range(num-2)
print fibs
抽象後
num = input('How many numbers do you want?')
print fibs(num)
6.2 抽象和結構
page = download_page()
freqs = computer_frequencies(page)
for word,freq in freqs:
print word,freq
6.3 建立函數
函數是可以調用,它執行某種行為并且傳回一個值。一般來說,内建的callable函數可以用來判斷函數是否可調用:
>>> import math
>>> x = 1
>>> y = math.sqrt
>>> callable(x)
False
>>> callable(y)
True
>>>
def hello(name):
return 'hello,' + name + '!'
建立一個名為hello的函數,他可以傳回一個将輸入的參數作為名字的問候語。可以像使用内建函數一樣使用它:
>>>print hello(‘world’)
hello,world
斐波那契函數
def fibs(num)
resule = [0,1]
for i in range(num-2):
result.append(result[-2] + result[-1])
return result
6.3.1 記錄函數
如果想要給函數寫文檔,讓後面使用該函數人能了解的話,可以加入注釋。另外一個方式就是直接寫上字元串。如果在函數的開頭寫下字元串,他就會作為函數的一部分進行存儲,這稱為文檔字元串。
def square(x):
'Calculates the square of the number x.'
return x*x
文檔字元串可以按如下方式通路:
>>>square._doc_
_doc_是函數屬性
内建的help函數是非常有用的。在互動解釋器中使用它,就可以得到關于函數,包括它的文檔字元串的資訊
>>>help(square)
6.3.2 并非真正函數的函數
數學意義上的函數,總在計算其參數後傳回點什麼。python的有些函數卻并不傳回任何東西。在其他語言中,這類函數可能有其他名字。但是python的函數就是函數,即便它從學術上并不是函數。沒有return語句,或者雖有return語句但return後邊沒有跟任何值得函數不傳回值:
def test():
print 'this is printed'
return 這裡的return函數隻起到結束函數的作用
print 'this is not'
>>>x = test()
This is printed
可以看到,第2個print語句被跳過了,但是如果test不傳回任何值,那麼x又引用什麼呢?
>>>x
>>>
沒東西,在仔細看看
>>>print x
None
是以所有的函數都傳回了東西,:當不需要他們傳回值得時候,它們就傳回None。
千萬不要被預設行為所迷惑。如果在if語句内傳回值,那麼要確定其他分支也有傳回值,這樣一來當調用者期待一個序列的時候,就不會意外的傳回None。
6.4 參數
6.4.1 值從哪裡來
寫在def語句中函數名後面的變量通常叫做函數的形式參數,而調用函數的時提供的值是實際參數,或者成為參數。
6.4.2 我能改變參數麼
在函數内内為參數賦予新值不會改變外部任何變量的值:
>>> def try_to_change(n): n = 'Mr,Gumby'
...
>>> name = 'Mrs,Entity'
>>> try_to_change(name)
>>> name
'Mrs,Entity'
在try_to_change内,參數n獲得了新值,但是它沒有影響到name變量。n實際上是個完全不同的變量,具體的工作方式類似于下面這樣:
>>> n = name #這句的作用基本上等于傳參數
>>> n = 'Mr.Gumby' #在函數内部完成的
結果是顯而易見的,當變量n改變的時候,變量name不變。同樣,當在函數内部把參數重綁定的時候,函數外的變量不會受到影響。
參數存儲在局部作用域。
字元串是不可變的,即無法被修改。
>>> def change(n): n[0] = 'Mr.Gumby'
>>> names = ['Mrs.Entity','Mrs.Thing']
>>> change(names)
>>> names
['Mr.Gumby', 'Mrs.Thing']
本例中,參數被改變了。這就是本例和前面例子中至關重要的差別。前面的例子中,局部變量被賦予了新值,但是這個例子中變量names所綁定的清單的确改變了。
>>> n = names
>>> n[0]='Mr.Gumby'
這類情況在前面已經出現了多次。當兩個變量同時引用一個清單的時候,他們的确是同時引用一個清單。如果想避免出現這種情況,可以複制一個清單的副本。當在序列中做切片的時候,傳回的切片總是一個副本。是以,如果你複制了整個清單的切片,将會得到一個副本:
>>> n = names[:]
現在n和name包含兩個獨立的清單,其值相等:
>>> n is names
>>> n == names
如果現在改變n,則不會影響到names:
>>> n
['Mrs.Entity', 'Mrs.Thing']
再用change試一下:
>>> change(names[:])
現在參數n包含一個副本,而原始的清單是安全的。
函數的局部名稱-----包括參數在内-----并不和外面的函數名稱沖突。
1.為什麼我想要修改參數
使用函數改變資料結構是将程式抽象化的好方法。假設需要編号一個存儲名字并且能用名字、中間名或姓查找聯系人的程式,可以使用下面的資料結構:
storage = {}
storage['first'] = {}
storage['middle'] = {}
storage['last'] = {}
storage這個資料結構的存儲方式是帶有3個鍵“first”“middle”和“last”的字典。每個鍵下面都又存儲一個字典。子字典中,可以使用名字作為鍵,插入聯系人清單作為值。比如要把我自己的名字加入這個資料結構,可以像下面這麼做:
>>> me = 'Magnus Lie Hetland'
>>> storage['first']['Magnus'] = [me]
>>> storage['middle']['Lie'] = [me]
>>> storage['last']['Hetland'] = [me]
每個鍵下面都存儲了一個以人名組成的清單。本例中,清單中隻有我。
現在如果想得到所有注冊的中間名為Lie的人,可以像下面這麼做:
>>> storage['middle']['Lie']
['Magnus Lie Hetland']
将人名加到清單中的步驟有點枯燥乏味,尤其是要加入很多姓名相同的人時,因為需要擴充已經存儲了那些名字的清單。例如,下面加入我姐姐的名字,而且假設不知道資料庫中已經存儲了什麼:
>>> storage['first'].setdefault('Anne',[]).append(my_sister)
>>> storage['middle'].setdefault('Lie',[]).append(my_sister)
>>> storage['last'].setdefault('Hetland',[]).append(my_sister)
>>> storage['first']['Anne']
['Anne Lie Hetland']
['Magnus Lie Hetland', 'Anne Lie Hetland']
如果要寫個大程式來這樣更新清單,那麼很顯然程式很快就會變得臃腫且笨拙不堪了。
抽象的要點就是隐藏更新時的繁瑣的細節,這個過程可以用函數實作。下面的例子就是初始化資料結構的函數:
def init(data):
data['first'] = {}
data['middle'] = {}
data['last'] = {}
上面的代碼隻是把初始化語句放到了函數中:
>>> storage = {}
>>> init(storage)
>>> storage
{'middle': {}, 'last': {}, 'first': {}}
可以看到,函數包辦了初始化的工作。
字典的鍵并沒有具體的順序。
在編寫存儲名字的函數前,先寫個獲得名字的函數:
def lookup(data,label,name):
return data[label].get(name)
标簽(比如middle)以及名字(比如Lie)可以作為參數提供給lookup函數使用,這樣會獲得包含全名的清單。換句話說,如果我的名字已經存儲了,可以像下面這樣做:
>>> lookup(storage,'middle','Lie')
注意,傳回的清單和存儲在資料結構中的清單是相同的,是以如果清單被修改了,那麼也會影響資料結構(沒有查詢到人的時候就問題不大了,因為函數傳回的是None)
def store(data,full_name):
names = full_name.split()
if len(names) == 2: names.insert(1,'')
labels = 'first','middle','last'
for label,name in zip(labels,names):
people = lookup(data,label,name)
if people:
people.append(full_name)
else:
data[label][name] = [full_name]
store函數執行以下步驟:
1.使用參數data和full_name進入函數,這兩個函數被設定為函數在外部獲得的一些值。
2.通過拆分full_name,得到一個叫names 的清單
3.如果names的長度為2(隻有首名和末名),那麼插入一個空字元串作為中間名。
4.将字元串“first”、“middle”和“last”作為元組存儲在labels中(也可以使用清單:這裡隻為了友善而去掉括号)
5.使用zip函數聯合标簽和名字,對于每一個(label,name)對,進行以下處理:
獲得屬于給定标簽和名字的清單。
将full_name添加到清單中,或者插入一個需要的新清單。
來試用一下剛剛實作的程式:
>>> Mynames={}
>>> init(Mynames)
>>> store(Mynames,'Magnus Lie Hetland')
>>> lookup(Mynames,'middle','Lie')
[‘Magnus Lie Hetland’]
好像可以工作:
>>> store(Mynames,'Robin Hood')
>>> store(Mynames,'Robin Locksley')
>>> lookup(Mynames,'first','Robin')
[‘Robin Hood’,‘Robin Locksley’]
>>> store(Mynames,'Mr.Gumby')
>>> lookup(Mynames,'middle','')
[‘Robin Hood’,‘Robin Locksley’,‘Mr。Gumby’]
可以看到,如果某些人的名字,中間名或姓相同,那麼結果中會包含所有這些人的資訊。
2.如果我的參數不可變
函數隻能修改參數對象本身。但是如果你得參數不可變----比如數字---又該怎麼辦? 這是沒有辦法的,這時候你應該從函數中傳回你需要的值(如果值多于一個話就以元組形式傳回)。例如,将變量的數值增1的函數可以這樣寫:
>>> def inc(x):return x+1
>>> foo=10
>>> foo = inc(foo)
>>> foo
11
如果真的想改變參數,那麼可以使用一點小技巧,即将值放置在清單中:
>>> def inc(x):x[0] = x[0] + 1
>>> foo = [10]
>>> inc(foo)
[11]
這樣就隻傳回新值,代碼看起來也比較清晰。
6.4.3關鍵字參數和預設值
目前為止我們所使用的參數都叫做位置參數,因為它們的位置很重要----事實上比它們的名字更加重要。本節中引入的這個功能可以回避為止問題,當你慢慢習慣使用這個功能以後,就會發現程式規模越大,它們的作用也就越大。
考慮下面的兩個函數:
def hello_1(greeting,name):
print '%s,%s!' % (greeting,name)
def hello_2(greeting,name):
兩個代碼所實作的是完全一樣的功能,隻是參數名字反過來了:
>>>hello_1('hello','world')
hello,world
>>>hello_2('hello','world')
有些時候,參數的順序是很難記住的,為了讓事情簡單些,可以提供參數的名字:
>>>hello_1(greeting=‘hello’,name=‘world’)
hello,world!
這樣一來,順序就完全沒影響了:
>>>hello_1(name=‘world’,greeting=‘hello’)
但參數名和值一定要對應:
>>>hello_2(greeting='hello',name='world')
world,hello
這類使用參數提供的參數叫做關鍵字參數。它的作用在于可以明确每個參數的作用,也就避免了下面這樣的奇怪的函數調用
>>>storre('Mr. Brainsample',10,20,13,5)
可以使用
>>>store(patient='Mr. Brainsample',hour=10,minute=20,day=13,mouth=5)
盡管這麼做打得字多了些,但是很顯然,每個參數的含義變得更加清晰,而且就算弄亂了參數的順序,對程式的功能也沒有任何影響。
關鍵字參數最厲害的地方在于可以在函數中給函數提供預設值:
def hello_3(greeting='hello',name='world'):
print '%s,%s!' % (greeting,name)
當參數具有預設值的時候,調用的時候就不用提供參數了!可以不提供,提供一些或提供所有的參數:
>>>hello_3()
>>>hello_3('Greetings')
Greetings,world
>>>hello_3('Greeting','universe')
Greeting,universe
可以看到,位置參數這個方法不錯-----除了在提供名字的時候就要提供問候語。但是如果隻想提供name參數,而讓greeting使用預設值該怎麼辦呢?
>>>hello_3(name='Gumby')
hello,Gumby
位置和關鍵字參數是可以聯合使用的。把位置參數放置在前面就可以了,如果不這樣做,解釋器會不知道它們到底是誰(也就是它們應該處的位置)。
除非完全清楚程式的功能和參數的意義,否則應該避免混合使用位置參數和關鍵字參數。一般來說,隻有在強制要求的參數個數比可修改的具有預設值的參數個數少的時候,才使用上面提到的參數書寫方法。
例如,hello函數可能需要名字作為參數,但是也允許使用者自定義名字,問候語和标點:
def hello_4(name,greeting=‘hello’,punctuation=‘!’):
print ‘%s,%s%s’ % (greeting,name,punctuation)
函數的調用方式很多:
>>>hello_4('Mars')
hello,Mars!
>>>hello_4('Mars','Howdy')
Howdy,Mars!
>>>hello_4('Mars','Howdy','...')
Howdy,Mars...
>>>hello_4('Mars',punctuation='.')
hello,Mars.
>>>hello_4()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
如果為name也賦予預設值,那麼最後一個語句就不會産生異常
6.4.4 收集參數
有些時候讓使用者提供任意數量的參數是很有用的,比如在名字存儲過程中,使用者每次隻能存一個名字。如果能像下面這樣存儲多個名字就更好了:
>>>store(data,name1,name2,name3)
使用者可以給函數提供任意多的參數,實作起來也不難。
試着像下面這樣定義函數:
def print_params(*params):
print params
這裡我隻指定了一個參數,但是前面加上了個星号。
>>>print_params('Testing')
('Testing',)
可以看到,結果作為元組列印出來,因為裡面有個逗号。是以在參數前使用星号就能列印出元組?那麼Params中使用多個參數看看會發生什麼:
>>>print_params(1,2,3)
(1,2,3)
參數前的星号将所有值放置在同一個元組中,可以說是将這些值收集起來,然後使用。
def print_params_2(title,*params):
print title
>>>print_params_2('Params:',1,2,3)
Params:
沒問題,是以星号的意思是收集其餘的位置參數。如果不提供任何供收集的元素,params就是個空元組:
>>>print_params_2('Nothing:')
Nothing:
()
>>>print_params_2('Hmm...',something=42)
會報錯
def print_params_3(**params):
至少解釋器沒有發牢騷,調用下
>>>print_params_3(x=1,y=2,z=3)
{'z':3,'x':1,'y':2}
傳回的是字典而不是元組。
def print_params_4(x,y,z=3,*pospar,**keypar):
print x,y,z
print pospar
print keypar
和我們期望的結果别無二緻
>>>print_params_4(1,2,3,5,6,7,foo=1,bar=2)
123
(5,6,7)
{'foo':1,'bar':2}
>>>print_params_4(1,2)
1 2 3
{}
怎麼實作多個名字同時存儲。
def store(data,*full_names):
for full_name in full_names:
if len(names) == 2:names.insert(1,'')
>>> d = {}
>>> init(d)
>>> store(d,'Han Solo')
但是現在可以這樣用
>>>store(d,'Luke Skywalker','Anakin Skywalker')
>>>lookup(d,'last','Skywalker')
['Luke Skywalker','Anakin Skywalker']
6.4.5 反轉過程
如何将參數收集為元組和字典,但是事實上,如果使用*和**的話,也可以執行相反的操作。那麼函數收集的逆過程是什麼樣?
def add(x,y): return x+y
比如說有包含由兩個要想加的數字組成的元組:
params = (1,2)
這個過程或多或少有點像我們上一節中介紹的逆過程。不是要收集參數,而是配置設定它們在“另一端”。使用*運算符就簡單了------不過是在調用而不是在定義時使用:
>>>add(*params)
3
對于參數清單來說工作正常,隻要擴充的部分是最新的就可以。可以使用同樣的技術來處理字典-----使用雙星号運算符。假設之前定義了hello_3,那麼可以這樣使用:
>>>params = {'name':'Sir Robin','greeting':'well met'}
>>>hello_3(**params)
Well met,Sir Robin!
在定義或者調用函數時使用星号(或者雙星号)僅傳遞元組或字典,是以可能沒遇到什麼麻煩:
>>>def with_stars(**kwds):
print kwds['name'],'is',kwds['age'],'years old'
>>>def without_stars(kwds):
>>>args = {'name':'Mr. Gumby','age':42}
>>>with_stars(**args)
Mr. Gumby is 42 years old
>>>without_stars(args)
可以看到,在with_stars中,我在定義和調用函數時都使用了星号。而在without_stars中兩處都沒用,但是得到了同樣的效果。是以星号隻在定義函數(允許使用不定數目的參數)或者調用(“分割”字典或者序列)時才有用。
使用拼接操作符“傳遞”參數很有用,因為這樣一來就不用關心參數的個數之類的問題,例如:
def foo(x,y,z,m=0,n=0):
print x,y,z,m,n
def call_foo(*args,**kwds):
print "Calling foo!"
foo(*args,**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 interval(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
>>>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'}
>>>print 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)
>>>params = (5,) * 2
>>>power(*params)
3125
>>>power(3,3,'hello,world')
Received redundant parameters:('hello,world',)
27
>>>interval(10)
[0,1,2,3,4,5,6,7,8,9]
>>>interval(1,5)
[1,2,3,4]
>>>interval(3,12,4)
[3,7,11]
>>>power(*interval(3,7))
Received redundant parameters:(5,6)
81
6.5 作用域
内建的vars函數可以傳回這個字典:
>>>x = 1
>>>scope = vars()
>>>scope['x']
1
>>>scope['x'] += 1
2
一般來說,vars所傳回的字典是不能修改的,因為根據官方的Python文檔的說法,結果是未定義的,換句話說,可能得不到想要的結果。
這類“不可見字典”叫做命名空間或者作用域。那麼到底有多少個命名空間?除了全局作用域外,每個函數調用都會建立一個新的作用域:
>>>def foo():x = 42
>>>foo()
這裡的foo函數改變了變量x,但是在最後的時候,x并沒有變。這是因為當調用foo的時候,新的命名空間就被建立了,它作用于foo内的代碼塊。指派語句x=42隻在内部作用域(局部命名空間)起作用,是以它并不影響外部作用域的x。函數内的變量被稱為局部變量。參數的工作元素類似于局部變量,是以用全局變量的名字作為參數名并沒有問題。
>>>def output(x):print x
>>>y = 2
>>>output(y)
但是如果需要在函數内部通路全局變量怎麼辦?而且隻想讀取變量的值(也就是說不想重綁定變量),一般來說是沒有問題的:
>>>def combine(parameter):print parameter + external
>>>external = 'berry'
>>>combine('Shrub')
Shrubberry
像這樣使用全局變量是誘發錯誤的引發原因。慎重使用全局變量。
屏蔽的問題
讀取全局變量一般來說并不是問題,但是還是有個會出問題的事情。如果局部變量或者參數的名字和想要通路的全局變量名相同的話,就不能直接通路了。全局變量會被局部變量屏蔽。
如果的确需要的話,可以使用globals函數擷取全局變量值,該函數的近親是vars,take傳回全局變量的字典(locals傳回局部變量的字典)。例如,如果前例中有個叫做parameter的全局變量,那麼就不能再combine函數内部通路該變量,因為你有一個與之同名的參數。必要時,能使用globals()['parameter']擷取:
>>>def combine(parameter):
print parameter + globals()['parameter']
>>>parameter = 'berry'
>>>combine(Shrub)
接下來讨論重綁定全局變量(使變量引用其他新值)。如果在函數内部将值賦予一個變量,它自動成為局部變量----除非告知Python将其聲明為全局變量。那麼怎麼才能告訴Python這是一個全局變量呢?
>>>x=1
>>>def change_global()
global x
x = x+1
>>>change_global()
嵌套作用域
Python的函數是可以嵌套的,也就是說可以将一個函數放在另一個裡面。下面是一個例子 :
def foo()
def bar():
print "hello,world"
bar()
嵌套一般來說并不是那麼有用,但它又一個很突出的應用,例如需要用一個函數“建立”另一個,也就意味着可以像下面這樣書寫函數:
def multiplier(factor):
def multiplyByFactor(number):
return number*factor
return multiplyByFactor
一個函數位于另外一個裡面,外層函數傳回裡層函數。也就是說函數本身被傳回了----但并沒有被調用。重要的是傳回的函數還可以通路它的定義所在的作用域,換句話說,它帶着它的環境和相關的局部變量。
每次調用外層函數,它内部的函數都被重新綁定,factor變量每次都有一個新的值。由于Python的嵌套作用域,來自外部作用域的這個變量,稍後會被内層函數通路。例如:
>>>double = multiplier(2)
>>>double(5)
10
>>>triple = multiplier(3)
>>>triple(3)
>>>multiplier(5)(4)
20
類似multiplyByFactor函數存儲于封閉作用域的行為叫做閉包
外部作用域的變量一般來說是不能進行重新綁定的。但是Python3.0中,nonlocal關鍵字被引入。它和global關鍵字的使用方式類似,可以讓使用者對外部作用域的變量進行指派。
6.6 遞歸
遞歸的定義包括它們自身定義内容的引用。由于每個人對遞歸的掌握程度不同,它可能會讓人大傷腦筋。
def recursion():
return recursion()
上述遞歸叫做無窮遞歸。有用的遞歸函數包含以下幾部分:
當函數直接傳回值時有基本執行個體
遞歸執行個體,包括一個或者多個問題最小部分的遞歸調用
這裡的關鍵就是将問題分解為小部分,遞歸不能永遠繼續下去,因為它總是以最小可能性問題結束,而這些問題又存儲在基本執行個體中。
是以讓函數調用自身。但是怎麼實作呢?
6.6.1 兩個經典:階乘和幂
首先,假設想要計算數n的階乘。n的階乘定義為n*(n-1)*(n-2)*。。。*1.很多數學應用中都會用到它。
def factorial(n):
result = n
for i in range(1,n):
result *= i
這個方法可行而且很容易實作。它的過程主要是;首先将result賦到n上,然後result依次與1到n-1的數相乘,最後傳回結果。 階乘數學定義:
1的階乘是1
大于1的數n的階乘是n乘n-1的階乘
可以看到,這個定義完全符合剛才所介紹的遞歸的兩個條件。
現在考慮如何定義實作為函數。了解定義本身後,實作其實很簡單:
def factorial(n):
if n==1:
return 1
return n * factorial(n-1)
這就是定義的直接實作。隻要記住函數調用factorial(n)是和調用factorial(n-1)不同的實體就行。
假設需要計算幂,就像内建的pow函數或者**運算符一樣。可以用很多種方法定義一個數的幂。先看一個簡單的例子:power(x,n) (x為n的幂次) 是x自乘n-1次的結果。是以power(2,3) 是2乘以自身3次:2*2*2=8
實作很簡單:
def power(x,n):
result = 1
for i in range(n):
result *= x
接下來該變成遞歸版本:
對于任意數字x來說,power(x,0)是1
對于任何大于0的數來說,power(x,n)是x乘以(x,n-1)的結果。
def pwoer(x,n):
if n == 0:
return x * power(x,n-1)
6.6.2 另外一個經典:二進制查找
def search(sequence,number,lower,upper):
if lower == upper:
assert number == sequence[upper]
return upper
middle = (lower + upper) // 2
if number > sequence[middle]:
return search(sequence,number,middle+1,upper)
return search(sequence,number,lower,middle)
完全符合定義。如果lower == upper,那麼傳回upper,也就是上限。注意,程式設計(斷言)所查找的數字一定會被找到(number == sequence[upper])。如果沒有到達基本執行個體的話,先找到middle,檢查數字是在左邊還是在右邊,然後使用新的上下限繼續調用遞歸過程。也可以将限制設為可選以友善用。隻要在函數定義的開始部分加入下面的條件語句即可:
def search(sequence,number,lower=0,upper=None):
if upper is None:upper = len(sequence)-1
現在如果不提供限制,程式會自動設定查找範圍為整個序列,看看行不行:
>>>seq = [34,67,8,123,4,100,95]
>>>seq.sort()
>>>seq
[4,8,34,67,95,100,123]
>>>search(seq,34)
>>search(seq,100)
5
但不必這麼麻煩,一則可以直接使用清單方法index,如果想要自己實作的話,隻要從程式的開始處循環疊代直到找到數字就行了。
當然可以,使用index沒問題了,但是隻使用循環可能效率有點低,剛才說過查找100内的一個數,隻需要7個問題即可。用循環的話,在最糟糕的情況下要問100個問題。
标準庫中的bisect子產品可以非常有效的實作二進制查找。
可以使用map函數将序列中的元素全部傳遞給一個函數:
>>>map(str,range(10))
['0','1','2','3','4','5','6','7','8','9']
filter函數可以基于一個傳回布爾值的函數對元素進行過濾。
>>>def func(x):
return x.isalnum()
>>>seq = ["foo","x41","?!","***"]
>>>filter(func,seq)
['foo','x41']
本例中,使用清單推導式可以不用專門定義一個函數:
>>>[x for x in seq if x.isalnum()]
事實上,還有個叫lambda表達式的特性,可以建立短小的函數。
>>>filter(lambda x: x.isalnum(),seq)
reduce函數一般來說不能輕松被清單推導式替代,但是通常用不到這個功能。它會将序列的前兩個元素與給定的函數聯合使用,并且将它們的傳回值和第三個元素繼續聯合使用,直到整個序列都處理完畢,并且得到一個最終結果。例如,需要計算一個序列的數字的和,可以使用reduce函數加上lambda x,y:x+y (繼續使用相同的數字):
>>>numbers = [72,101,108,108,111,44,32,119,111,114,108,100,33]
>>>reduce(lambda x,y: x+y,numbers)
1161
本文轉自潘闊 51CTO部落格,原文連結:http://blog.51cto.com/pankuo/1661440,如需轉載請自行聯系原作者