- 論壇徽章:
- 0
|
首先來復(fù)習(xí)python中函數(shù)的定義。在python中,有兩種方式可以用來定義一個(gè)函數(shù)。用def可以定義有名字的函數(shù)。用lambda可以定義匿名函數(shù)。由于在實(shí)際調(diào)用一個(gè)函數(shù)時(shí),實(shí)參的個(gè)數(shù)可能是一個(gè)也可能是兩個(gè),還可能是多個(gè)。因此在形參前面加上*或**來處理這個(gè)情況。比如:
def test(*x): if len(x)>0: for c in x: print c else: print None上面定義的函數(shù)的功能是把參數(shù)一個(gè)一個(gè)打出來(通過print),這個(gè)函數(shù)可以使用沒有實(shí)參的形式調(diào)用,比如:test();也可以使用一個(gè)實(shí)參的形式調(diào)用,比如:test(4);還可以以多個(gè)實(shí)參的形式調(diào)用,比如:test(1,2,3,4)。但不管是哪種方式,在調(diào)用的時(shí)候?qū)嶋H參數(shù)的個(gè)數(shù)都已經(jīng)確定下來了,一個(gè)就是一個(gè),兩個(gè)就是兩個(gè),F(xiàn)在假設(shè)在一個(gè)程序中有一個(gè)list,比如叫userInput,這個(gè)list中的成員是由用戶在使用程序時(shí)交互確定的,在運(yùn)行中可能有一個(gè)成員,也可能有多個(gè)成員,這一切都要看用戶怎么操作,F(xiàn)在要用test函數(shù)把userInput打出來。那么使用下面這個(gè)形式是不行的:
這只會(huì)把userInput作為一個(gè)整體打出來,比如說用戶選擇后userInput=[1,2],那么打出來的是[1,2],而我們想要的結(jié)果是:
1
2
這種形式的。很自然的,可能會(huì)想到應(yīng)該用一個(gè)for循環(huán):
for c in userInput: test(c)這當(dāng)然能實(shí)現(xiàn)上面要求的功能。不過,在python中還有更簡單的辦法,就是使用內(nèi)置函數(shù)apply:
表示把userInput作為test的參數(shù),也就是說比如在程序運(yùn)行時(shí),userInput得到的值是[1,2,3],那么就相當(dāng)于test(1,2,3)。如果userInput得到的實(shí)際值是[1,2],那么就相當(dāng)于test(1,2)。
總之,定義函數(shù)時(shí)在形式參數(shù)面前加上*或**是為了,在調(diào)用這個(gè)函數(shù)的時(shí)候可以靈活地提供實(shí)參的個(gè)數(shù)。而apply則是為了可以用不定個(gè)數(shù)的實(shí)際參數(shù)來調(diào)用函數(shù)。有點(diǎn)暈。(我也想不到一個(gè)更好的表達(dá)方式,或更好的例子。書中說,在后面會(huì)看到應(yīng)用apply強(qiáng)大能力的例子)
其實(shí)還可以更簡單,連apply都不用,直接寫成:
這表示的意思就是apply(test,userInput)。可以把函數(shù)定義的中形參前面的*理解為“打包”操作,也就是把多余的實(shí)參都打包在一個(gè)tuple中;可以把調(diào)用時(shí)的*理解為“解包”的操作,比如說這里表示:調(diào)用test函數(shù),實(shí)參是對userInput“解包”后所得的東西。當(dāng)然對于**也可以以相同的方式理解。在test(**D)的形式中,D必須為一個(gè)dictionrary,相當(dāng)于test(key1=value1,key2=value2...)。
(其實(shí)我不知道為什么會(huì)在python中出現(xiàn)這種古怪的東西,看這本書的前面幾章時(shí),覺得python太棒了,真的是很簡潔。不過越往后看,覺得python越來越背離了它“簡單”的宗旨)
map函數(shù)在前面介紹過了,基本的形式是:
- map(function, seq1,seq2...)
map實(shí)際上是python中一大類函數(shù)的一個(gè)代表,這類函數(shù)可以被稱為函數(shù)工具。常用的函數(shù)中,同類的還有filter和reduce。先來看filter:
它返回一個(gè)序列類型的值。表示把seq中的每個(gè)成員依次代入function中,如果為真則它將成為返回序列中的一個(gè)成員。filter函數(shù)類似于:
res = list()for x in seq: if func(x): res.append(x)res如果filter的第一個(gè)實(shí)參為None,則表示把seq的中真值挑出來。
再來看reduce函數(shù):
- reduce(function, sequence[, initial])
表示把sequence按照function提供的規(guī)則進(jìn)行計(jì)算,最后算出一個(gè)值來。這個(gè)function必須具有兩個(gè)參數(shù),比如:
L = [1,2,3,4,5]reduce((lambda x,y: x*y),L) ===> 120這就相當(dāng)于把乘法應(yīng)用于L的各個(gè)成員中,也就是1*2*3*4*5。reduce的第三個(gè)參數(shù)表示一個(gè)初始值。即初始值*1*2*3*4*5。python中有一個(gè)叫operator的模塊,其中提供了很多操作,比如加法叫add,乘法叫mul。上面的這一小段代碼可以寫成:
import operatorL = range(6)[1:]reduce(operator.mul, L)
從上面介紹可以看出,map、filter或者reduce實(shí)際都是對序列類型數(shù)據(jù)的成員進(jìn)行操作。由于這種操作在python是非常普遍的,因此在python 2.0后提供了一種叫"list的再構(gòu)造"(英文名叫l(wèi)ist comprehansions,不知道怎么翻合適)的語法形式來進(jìn)行類似的操作。假設(shè)L = [1,2,3,4],那么:map((lambda x: x**2), L)就可以寫成:[x**2 for x in L]。這就是所謂的"list的再構(gòu)造"。認(rèn)真來看看這個(gè):
- [x**2 for x in L] ===> [1,4,9,16]
首先,這個(gè)表達(dá)式用[]括起來了,表示這個(gè)表達(dá)式返回的結(jié)果是一個(gè)list。其次,后面一部分,也就是for x in L,很明顯是把L中的成員依次取出來。第三,前面一部分,也就是x**2,表示對x的操作是"平方"。然后以每次計(jì)算后的值作為目標(biāo)list的成員。其實(shí)可以把這一部分看成是目標(biāo)list的“通項(xiàng)公式”,通過對這個(gè)“通項(xiàng)公式”代入不同的值,可以得到不同的結(jié)果。而這個(gè)“不同的值”就是由后面的部分,即for x in L,確定的。
- [(x**2,x**3) for x in L] ===> [(1, 1), (4, 8), (9, 27), (16, 64)]
當(dāng)然大名頂頂?shù)?quot;list再構(gòu)造"還有別的功能,它可以加上條件從句(if語句),比如:
- [x**2 for x in L if x%2==0] ===>[4,16]
"list再構(gòu)造"還可以嵌套for這一部分,比如:
- [x*y for x in L for y in L] ===> [1, 2, 3, 4, 2, 4, 6, 8, 3, 6, 9, 12, 4, 8, 12, 16]
相當(dāng)于:
res = []for x in L: for y in L: res.append(x*y)res當(dāng)然,嵌套for的"list再構(gòu)造"也可以加上條件判斷。比如:
- [x*y for x in L if x%2==0 for y in L if y%2 != 0] ===>[2, 6, 4, 12]
這個(gè)式子已經(jīng)相當(dāng)復(fù)雜,因此在以程序可讀性著稱的python世界中,最好不要使用這種東西。不過,也可見用“l(fā)ist再構(gòu)造”也可以實(shí)現(xiàn)for或者map和filter的功能,但是list再構(gòu)造的效率最高,其次map、filter這些內(nèi)置函數(shù),再次才是for。
本文來自ChinaUnix博客,如果查看原文請點(diǎn):http://blog.chinaunix.net/u/2809/showart_81363.html |
|