文中知識(shí)點(diǎn)和代碼示例學(xué)習(xí)自慕課網(wǎng),python進(jìn)階部分(http://www.imooc.com/learn/317) 學(xué)習(xí)筆記
把函數(shù)作為參數(shù)
例1:求兩個(gè)數(shù)字絕對(duì)值的和(abs(x)+abs(y))
def add(x,y,f):
return f(x) + f(y)
# add 函數(shù)的x,y,f 參數(shù)可以是任何值,如果 f 是個(gè)函數(shù),則 x,y 兩個(gè)參數(shù)分別帶入到 f 函數(shù)中求值后,再做為 add 函數(shù)的參數(shù)傳入。
print add(-5,9,abs)
例2:求√x + √y的值
import math
def add(x, y, f):
return f(x) + f(y)
print add(25, 9, math.sqrt)
map() 函數(shù)
它接收一個(gè)函數(shù)f和一個(gè)list,并通過(guò)把函數(shù)f依次作用在list的每個(gè)元素上,得到一個(gè)新的list 并返回。
例1:把列表總每個(gè)元素都取二次方
def f(x):
return x * x
print map(f,[1,2,3,4,5,6,7,8,9])
#[1, 4, 9, 16, 25, 36, 49, 64, 81]
例2:將列表中的字符串都變?yōu)槭鬃帜复髮?/p>
def format_name(s):
return s.title()
print map(format_name, ['adam', 'LISA', 'barT'])
#['Adam', 'Lisa', 'Bart']
reduce() 函數(shù)
reduce()函數(shù)接收的參數(shù)和map()類似,一個(gè)函數(shù)f,一個(gè)list,但行為和map()不同,reduce()傳入的函數(shù)f必須接收兩個(gè)參數(shù),reduce()對(duì)list的每個(gè)元素反復(fù)調(diào)用函數(shù)f,并返回最終結(jié)果值。
例1:求一個(gè)列表中所有元素的和
def f(x,y):
return x + y
print reduce(f,[1,2,3,4,5])
先計(jì)算頭兩個(gè)元素:f(1, 2),結(jié)果為3;
再把結(jié)果和第3個(gè)元素計(jì)算:f(3, 3),結(jié)果為6;
再把結(jié)果和第4個(gè)元素計(jì)算:f(6, 4),結(jié)果為10;
再把結(jié)果和第5個(gè)元素計(jì)算:f(10, 5),結(jié)果為15;
由于沒(méi)有更多的元素了,計(jì)算結(jié)束,返回結(jié)果15.
#15
filter() 函數(shù)
filter()函數(shù)接收一個(gè)函數(shù)f和一個(gè)list,這個(gè)函數(shù)f的作用是對(duì)每個(gè)元素進(jìn)行判斷,返回True或False,filter()根據(jù)判斷結(jié)果自動(dòng)過(guò)濾掉不符合條件的元素,返回由符合條件元素組成的新list。
例:刪除一個(gè)list中的偶數(shù),保留奇數(shù)
def is_odd(x):
return x % 2 == 1
print filter(is_odd(),[1,2,3,4,5,6,6,7,8,9])
#[1, 3, 5, 7, 9]
可以用來(lái)刪除一個(gè)列表中我們不需要的元素。也可以使用如下方法來(lái)完成:
[i for i in [1,2,3,4,5,6,6,7,8,9] if i % 2 ==1 ]
#[1, 3, 5, 7, 9]
sorted() 函數(shù)
Python內(nèi)置的 sorted()函數(shù)可對(duì)list進(jìn)行排序:
>>>sorted([36, 5, 12, 9, 21])
[5, 9, 12, 21, 36]
但 sorted()也是一個(gè)高階函數(shù),它可以接收一個(gè)比較函數(shù)來(lái)實(shí)現(xiàn)自定義排序,比較函數(shù)的定義是,傳入兩個(gè)待比較的元素 x, y,如果 x 應(yīng)該排在 y 的前面,返回 -1,如果 x 應(yīng)該排在 y 的后面,返回 1。如果 x 和 y 相等,返回 0。
例:實(shí)現(xiàn)倒序排列
def reversed_cmp(x, y):
if x > y:
return -1
if x < y:
return 1
return 0
print sorted([36, 5, 12, 9, 21], reversed_cmp)
#[36, 21, 12, 9, 5]
其他方法:
# 先排序,后反轉(zhuǎn)
>>> sorted([36, 21, 12, 9, 5])[::-1]
[36, 21, 12, 9, 5]
# 使用reversed()方法,返回一個(gè)迭代器。 如果使用reverse(),是直接修改原列表,不會(huì)返回新的列表
>>> list(reversed(sorted([36, 5, 12, 9, 21])))
[36, 21, 12, 9, 5]
返回函數(shù)
Python的函數(shù)不但可以返回int、str、list、dict等數(shù)據(jù)類型,還可以返回函數(shù)!
例1:定義一個(gè)函數(shù) f(),我們讓它返回一個(gè)函數(shù) g
def f():
print 'call f()...'
# 定義函數(shù)g:
def g():
print 'call g()...'
# 返回函數(shù)g:
return g
>>> x = f() # 調(diào)用f()
call f()...
>>> x # 變量x是f()返回的函數(shù)g:
<function g at 0x1037bf320>
>>> x() # x指向函數(shù)g,因此可以調(diào)用
call g()... # 調(diào)用x()就是執(zhí)行g(shù)()函數(shù)定義的代碼
例2:寫一個(gè)函數(shù)calc_prod(lst),它接收一個(gè)list,返回一個(gè)函數(shù),返回函數(shù)可以計(jì)算參數(shù)的乘積。
def calc_prod(lst):
def f(x,y):
return x * y
def chengji():
return reduce(f,lst)
return chengji
f = calc_prod([1, 2, 3, 4])
print f()
閉包
例如上一小節(jié)返回函數(shù)中的例2,chengji內(nèi)層函數(shù)引用了外層函數(shù)calc_prod的lst變量。像這種內(nèi)層函數(shù)引用了外層函數(shù)的變量(參數(shù)也算變量),然后返回內(nèi)層函數(shù)的情況,稱為閉包(Closure)。
閉包的特點(diǎn):返回的函數(shù)還引用了外層函數(shù)的局部變量,所以,要正確使用閉包,就要確保引用的局部變量在函數(shù)返回后不能變。舉例如下:
# 希望一次返回3個(gè)函數(shù),分別計(jì)算1x1,2x2,3x3:
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
#9 9 9
原因就是當(dāng)count()函數(shù)返回了3個(gè)函數(shù)時(shí),這3個(gè)函數(shù)所引用的變量i的值已經(jīng)變成了3。由于f1、f2、f3并沒(méi)有被調(diào)用,所以,此時(shí)他們并未計(jì)算i*i,當(dāng) f1 被調(diào)用時(shí):
>>> f1()
9 # 因?yàn)閒1現(xiàn)在才計(jì)算i*i,但現(xiàn)在i的值已經(jīng)變?yōu)?
因此,返回函數(shù)不要引用任何循環(huán)變量,或者后續(xù)會(huì)發(fā)生變化的變量。因此改成如下代碼:
# 法一
def count():
fs = []
for i in range(1, 4):
def f(j):
# 借助函數(shù)f來(lái)避免引用循環(huán)變量i
def g():
return j*j
return g
r = f(i)
fs.append(r)
return fs
f1, f2, f3 = count()
print f1(), f2(), f3()
#1 4 9
# 法二
def count():
fs = []
def f(j):
def g():
return j*j
return g
for i in range(1, 4):
r = f(i)
fs.append(r)
return fs
f1, f2, f3 = count()
print f1(), f2(), f3()
#1 4 9
匿名函數(shù) lambda
高階函數(shù)可以接收函數(shù)做參數(shù),有些時(shí)候,我們不需要顯式地定義函數(shù),直接傳入匿名函數(shù)更方便。
在Python中,對(duì)匿名函數(shù)提供了有限支持。還是以map()函數(shù)為例,計(jì)算f(x)=x*x時(shí),除了定義一個(gè)f(x)的函數(shù)外,還可以直接傳入匿名函數(shù)
>>> a = lambda x:x*x
>>> a(5)
25
>>> map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])
[1, 4, 9, 16, 25, 36, 49, 64, 81]
匿名函數(shù)有個(gè)限制,就是只能有一個(gè)表達(dá)式,不寫return,返回值就是該表達(dá)式的結(jié)果。
def is_not_empty(s):
return s and len(s.strip()) > 0
filter(is_not_empty, ['test', None, '', 'str', ' ', 'END'])
返回函數(shù)的時(shí)候,也可以返回匿名函數(shù):
# 使用 if..else 來(lái)實(shí)現(xiàn)abs函數(shù)的功能
>>> myabs = lambda x: -x if x < 0 else x
>>> myabs(-1)
1
>>> myabs(1)
1
例:使用匿名函數(shù)簡(jiǎn)化代碼
# 源代碼
def is_not_empty(s):
return s and len(s.strip()) > 0
filter(is_not_empty, ['test', None, '', 'str', ' ', 'END'])
# 使用匿名函數(shù)
print filter(lambda x:x and len(x.strip()) > 0,['test', None, '', 'str', ' ', 'END'])
# 使用 for..in..if 語(yǔ)句
print [i for i in ['test', None, '', 'str', ' ', 'END'] if i and len(i.strip())>0]
偏函數(shù)
當(dāng)一個(gè)函數(shù)有很多參數(shù)時(shí),調(diào)用者就需要提供多個(gè)參數(shù)。如果減少參數(shù)個(gè)數(shù),就可以簡(jiǎn)化調(diào)用者的負(fù)擔(dān)。
比如,int()函數(shù)可以把字符串轉(zhuǎn)換為整數(shù),當(dāng)僅傳入字符串時(shí),int()函數(shù)默認(rèn)按十進(jìn)制轉(zhuǎn)換:
>>> int('12345')
12345
但int()函數(shù)還提供額外的base參數(shù),默認(rèn)值為10。如果傳入base參數(shù),就可以做 N 進(jìn)制的轉(zhuǎn)換:
>>> int('12345', base=8)
5349
>>> int('12345', 16)
74565
假設(shè)要轉(zhuǎn)換大量的二進(jìn)制字符串,每次都傳入int(x, base=2)非常麻煩,于是,我們想到,可以定義一個(gè)int2()的函數(shù),默認(rèn)把base=2傳進(jìn)去:
def int2(x, base=2):
return int(x, base)
# 這樣,我們轉(zhuǎn)換二進(jìn)制就非常方便了:
>>> int2('1000000')
64
>>> int2('1010101')
85
functools.partial就是幫助我們創(chuàng)建一個(gè)偏函數(shù)的,不需要我們自己定義int2(),可以直接使用下面的代碼創(chuàng)建一個(gè)新的函數(shù)int2:
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85
所以,functools.partial可以把一個(gè)參數(shù)多的函數(shù)變成一個(gè)參數(shù)少的新函數(shù),少的參數(shù)需要在創(chuàng)建時(shí)指定默認(rèn)值,這樣,新函數(shù)調(diào)用的難度就降低了。