python中的函數式編程,python函數式編程
函數式編程的一個特點就是,允許
1. 把函數本身作為參數傳入另一個函數!
2. 返回一個函數!
python中主要有以下幾方面的函數式編程方法,下面將從概念,作用和範例程式碼來說明.
一. 高階函數(函數作為參數)
這個其實對應的是第一個特點,即“把函數本身作為參數傳入另一個函數”,類似於C++中的”函數指標”,這樣的技術很容易實現模板技術,最常用到的就是sort函數,看下面的代碼,python中的sort對於int類型預設是從小到大的順序,如果我們想自訂順序呢?這個時候”高階函數”的強大就體現出來了.
#!usr/bin/env pythonimport mathL = [0, -1, 8, 20, -99]print sort(L, abs)
如上所示,abs這個函數被當做一個參數 傳進了sort中作為sort的一個參數,為什麼可以這樣?其實準備來說,是把abs函數的函數名作為參數,在python裡面,一個定義好的參數其實是一個儲存在記憶體中的變數,比如下面的互動:
>>> def fun():... print 'haha'... >>> fun<function fun at 0x7faf4667f578>>>>
具體的地址很可能跟我列印的不一樣,不過實質都一樣是一個地址,這也驗證了上面“函數名是一個變數”的說法.
1. map和reduce
這兩個內建函數是高階函數應用的經典例子,直接看範例程式碼:
#!usr/bin/env pythondef lala(x, y=2015): return x % yprint map(lala, range(2000, 2030)
其運行結果是:[2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
#!usr/bin/env pythondef plus(x, y): return x * yprint reduce(plus, range(1, 5))
其結果是:24,也就是1*2*3*4
其實從上面的代碼和運行結果就可以猜到這兩個函數的作用到底是什麼了,它們都接受兩個參數.第一個是一個函數名,第二個是一個列表,map將列表中的每一個元素作為函數的參數,返回每個函數傳回值的列表,注意傳入的函數的參數的個數,如果需要多個的話,那麼對應的列表就要對應做變化.
至於reduce,對傳入的函數的要求比較嚴格,需要恰好是偶數(有預設參數的不計在內),因為它做的事情,就是順序地從列表執行,將上一次執行完的傳回值與列表中的下一個元素再傳進去計算,上面的代碼的等效於
a = plus(1, 2) #首先是取第1,第2個元素b = plus(a, 3) #然後傳回值作為第一個參數,取下一個元素做為第二個參數c = plus(b, 4) #同上print c
至於這樣的兩個函數有什麼作用呢?只能提供一點grammer sweet嗎?答案是否定的!!!詳情請看度娘
我覺得其中很有意義的一句話是“它極大地方便了編程人員在不會分布式並行編程的情況下,將自己的程式運行在分布式系統上”.
2. 過濾器filter
顧名思義,過濾器就是根據你提供的規則,將需要的資料保留下來,filter同樣是接受兩個參數,第一個是一個返回布爾值的函數,第二個是一個元素類型為該布爾函數的參數的列表.先看範例程式碼:
#!usr/bin/env pythonimport mathdef is_prime(n): if n <= 1: return False for i in range(2, (int)(math.sqrt(n)+1)): if n % i ==0: return False return Trueprint filter(is_prime, range(1, 101))
其結果為: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
也就是1到100的所有素數.
二. 如何返回一個函數1. lambda運算式(匿名方法)
python中支援簡單lambda運算式,比如這樣:
>>> f = lambda x : x * x>>> f<function <lambda> at 0x7faf4667fb18>>>>
一目瞭然,沒錯,它就是用來產生匿名方法的!
“…lambda expressions and anonymous methods are really just two words for the same thing. The only thing that differs is, “What does the syntax look like ” And the lambda expressions are a further evolution of the syntax.But underneath, they do the same thing. They generate methods. You know, they’re in-line methods.”
——C#首席架構師Anders Hejlsberg
由此引出一個閉包的概念,它是怎麼返回一個函數的呢?還是看代碼來的直接吧:
#!usr/bin/env pythondef f(x): def fun(): return x * x return fun>>> f(1).__name__'fun'>>>
看到這裡應該很清晰了吧,利用閉包,我們可以直接使用所在函數的參數儲存當時的函數調用情境!!!至於有什麼用,就見仁見智了,用到了,自然會明白其中的妙處,只是我自己現在也還不清楚T_T.
2. 裝飾器
上面的匿名方法返回的是”原生的函數內容”,那麼如果想在不改變原始碼的情況下,增加函數的功能呢?有沒有辦法,裝飾器就誕生了…用法直接看代碼就知道了:
#!usr/bin/env pythondef log(func): def haha(*args, **kw): print 'call %s():' % func.__name__ return func(*args, **kw) return haha@logdef now(x): print 'wo cao, %s' % x>>> now('12點了')call now():wo cao, 12點了>>>
用了@文法,表示拿log來裝飾now函數.那log函數其實就是上面說過的”閉包”啦.這裡只是簡單地給函數加了一個”調用前聲明”,自己要添加別的自然也是類似的方法.
值得注意的是:
a) “裝飾的原理”
@log其實可以看成發生了這樣一件事:now = log(now),也就是原來的now函數被覆蓋了,變成了經過log閉封裝飾過的一個函數.
b) 函數傳參???
這裡用了”萬能的參數列表”:(*args, **kw),也就是無論你需要修飾的函數的參數列表是怎樣的,都可以被修飾!
更詳細的講解見廖大大(LDD).
3. 偏函數
其實這個名字是從廖雪峰大大的教程那裡看來的,感覺有點直譯過頭了,不過離散數學裡面的偏序好像也是這麼直譯了,就不糾結命名問題了.它是幹什麼的呢?這次返回的又是什麼樣的函數?
當函數的參數個數太多,需要簡化時,使用functools.partial可以建立一個新的函數,這個新函數可以固定住原函數的部分參數,從而在調用時更簡單。
—— from LDD
看一個簡單例子就能理解了:
#!usr/bin/env pythonimport functools #注意要import這個模組#example code is from LDDint2 = functools.partial(int, base=2)>>> import functools>>> int2 = functools.partial(int, base=2)>>> int2('1000000')64>>> int2('1010101')85
將二進位轉換為十進位,固定了base這個參數的值,相對於”全”,叫做”偏”也很直觀了!
至此,python中的函數式編程的兩大方面已經簡單介紹完了.謝謝
閱讀~碎覺( ⊙ o ⊙ )!