Day-6: 函數式編程
函數式編程就是封裝成一個個函數,一次調用來完成復雜任務。
函數式編程的一個特點是,允許把函數本身作為參數傳入另一個函數,還允許返回一個函數!
- 高階函數
高階函數就是將函數的變量名作為參數傳入,內部再對該函數進行調用的函數。
一個簡單的高階函數如下:
def add(x, y, f): return f(x) + f(y)
x ==> -5 y ==> 6 f ==> abs f(x) + f(y) ==> abs(-5) + abs(6) ==> 11
>>> add(-5, 6, abs)11
Python內建了map()、reduce()、filter()和sorted()等高階函數。
map()
函數接收兩個參數,一個是函數,一個是序列,map
將傳入的函數依次作用到序列的每個元素,並把結果作為新的list返回。
>>> def f(x): ... return x * x ... >>> map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]) [1, 4, 9, 16, 25, 36, 49, 64, 81]
map()
作為高階函數,事實上它把運算規則抽象了,因此,我們不但可以計算簡單的f(x)=x2,還可以計算任意復雜的函數,比如,把這個list所有數字轉為字符串:
>>> map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9]) [‘1‘, ‘2‘, ‘3‘, ‘4‘, ‘5‘, ‘6‘, ‘7‘, ‘8‘, ‘9‘]
reduce把一個函數作用在一個序列[x1, x2, x3...]上,這個函數必須接收兩個參數,reduce把結果繼續和序列的下一個元素做累積計算,其效果就是:
>>> def fn(x, y): ... return x * 10 + y ... >>> reduce(fn, [1, 3, 5, 7, 9]) 13579
filter()函數是用於過濾序列的,傳入一個函數和一個序列。filter()
True
還是False
決定保留還是丟棄該元素,如:
def not_empty(s): return s and s.strip() filter(not_empty, [‘A‘, ‘‘, ‘B‘, None, ‘C‘, ‘ ‘]) # 結果: [‘A‘, ‘B‘, ‘C‘]
sorted()函數是用於對序列進行排序,傳入一個序列和一個默認函數為cmp的函數。只傳入序列時,進行默認排序,如下:
>>> sorted([36, 5, 12, 9, 21])
[5, 9, 12, 21, 36]
傳入序列和函數時,依據傳入的函數進行排序,如下:
def reversed_cmp(x, y): if x > y: return -1 if x < y: return 1 return 0
>>> sorted([36, 5, 12, 9, 21], reversed_cmp)
[36, 21, 12, 9, 5]
- 返回函數
一個函數的返回值不是變量,而是函數。這種方式可以構成“閉包”,對程序有極大的應用,如裝飾器。
但是需要註意的是:返回函數不要引用任何循環變量,或者後續會發生變化的變量。
def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f) return fs f1, f2, f3 = count()
>>> f1() 9 >>> f2() 9 >>> f3() 9
如果一定要引用循環變量的話,方法是再創建一個函數,用該函數的參數綁定循環變量當前的值,無論該循環變量後續如何改變,已綁定到函數參數的值不變。
>>> def count(): ... fs = [] ... for i in range(1, 4): ... def f(j): ... def g(): ... return j*j ... return g ... fs.append(f(i)) ... return fs ... >>> f1, f2, f3 = count() >>> f1() 1 >>> f2() 4 >>> f3() 9
- 匿名函數
當Python中,傳入函數不需要顯式地定義時,就可以利用匿名函數直接帶入;同時,由於匿名函數沒有名字,不會出現函數名字沖突的情況。
匿名函數的格式是:lambda x: x * x
- 裝飾器
當希望在已有的函數基礎上增加一部分功能,但是又不想重新改函數時,就可以使用裝飾器,進行動態的修改,例如:對一個函數增加日誌打印的功能。
def log(func): def wrapper(*args, **kw): print ‘call %s():‘ % func.__name__ return func(*args, **kw) return wrapper
@log def now(): print ‘2013-12-25‘
>>> now()
call now():
2013-12-25
另外,要改變打印內容時,用到3層套用,就是下面的情況:
import functools
def log(text):
def decorator(func):
@functools.wraps(func) # 裝飾後不改變原函數的內置屬性
def wrapper(*args, **kw):
print ‘%s %s():‘ % (text, func.__name__)
return func(*args, **kw)
return wrapper
return decorator
@log(‘execute‘) def now(): print ‘2013-12-25‘
>>> now()
execute now():
2013-12-25
- 偏函數
對於已有的函數,如果有默認的參數值,但是我們最近常調用的是另一個參數值時,可以使用偏函數,生成默認值為另一個參數的新函數。
>>> import functools >>> int2 = functools.partial(int, base=2) >>> int2(‘1000000‘) 64 >>> int2(‘1010101‘) 85
相當於:
def int2(x, base=2): return int(x, base)
Day-6: 函數式編程