Python是一門多範式的編程語言,它同時支持過程式、面向對象和函數式的編程範式。所以,在Python中提供了不少符合 函數式編程 風格的特性和工具。html
如下是對 Python中的函數式編程 的簡要總結,關於這一主題更全面的討論能夠參考 Functional Programming HOWTO。python
除了 Python基礎:函數 中介紹的 def語句,Python還提供了另一種定義函數的方法: lambda表達式。express
lambda表達式的語法以下:編程
lambda [arguments]: expression
與def語句相似,lambda表達式建立的函數:markdown
可是lambda表達式與def語句之間,也存在不少顯著的差別:閉包
差別點 | 函數(lambda表達式) | 函數(def語句) |
---|---|---|
函數體 | 只能是單行表達式(expression) | 能夠是任意複雜的語句(statement) |
函數返回值 | 返回值就是函數體中的表達式的求值結果 | 由函數體中的return語句指定 返回值 |
函數名 | 定義後直接返回函數對象(匿名函數) | 定義後自動爲函數對象綁定函數名 |
函數定義位置 | 能夠在任何容許函數對象出現的位置定義(支持即時定義,即時調用) | 只能在容許語句出現的位置定義(先定義,後調用) |
用途 | 多用於一次性使用的簡單函數 | 適用於一切函數和類方法 |
如下是lambda表達式的簡單示例:app
# def語句 >>> def func(x, y): return x + y # 自動綁定函數名爲func ... >>> func <function func at 0xb76eff7c> >>> func(1, 2) # 先定義,後調用 3 # lambda表達式 >>> lambda x, y: x + y # 匿名函數(直接返回函數對象) <function <lambda> at 0xb76ef0d4> >>> (lambda x, y: x + y)(1, 2) # 即時定義,即時調用 3 >>> f = lambda x, y: x + y # 手動綁定函數名 >>> f(1, 2) # 也能夠先定義,後調用 3 >>> >>> ((lambda x: (lambda y: x + y))(1))(2) # 嵌套定義的lambda(較複雜,儘可能避免) 3
函數原型:filter(function, iterable)編程語言
說明:返回一個由iterable中的某些元素組成的列表,這些元素使得function返回True。若iterable爲字符串(或元組),則返回字符串(或元組);不然,老是返回列表。若是function爲None,則默認爲恆等函數(identity function,相似 f(x) = x)。ide
示例:函數式編程
# for循環版本 >>> res = [] >>> for x in 'a1b2c3d4e5f6': ... if x.isalpha(): ... res.append(x) ... >>> res ['a', 'b', 'c', 'd', 'e', 'f'] # filter版本 s = 'a1b2c3d4e5f6' >>> filter((lambda x: x.isalpha()), s) # iterable爲字符串,則返回字符串 'abcdef' >>> filter((lambda x: x.isalpha()), tuple(s)) # iterable爲元組,則返回元組 ('a', 'b', 'c', 'd', 'e', 'f') >>> filter((lambda x: x.isalpha()), list(s)) # iterable爲其餘迭代對象,則返回列表 ['a', 'b', 'c', 'd', 'e', 'f'] >>> filter(None, list(s)) # function爲None,則默認爲恆等函數 ['a', '1', 'b', '2', 'c', '3', 'd', '4', 'e', '5', 'f', '6']
函數原型:map(function, iterable, ...)
說明:逐個以iterable中的元素爲參數調用function,並返回結果的列表。若是存在多個iterable,則以最長的爲準(其餘不足的補None),逐個並行取出元素做爲參數調用function(如map(function, iter1, iter2)會返回列表[function(iter1[0], iter2[0]), function(iter1[1], iter2[1]), ...])。若是function爲None,則默認爲恆等函數。
示例:
# for循環版本 >>> res = [] >>> for x in [1, 2, 3, 4, 5]: ... res.append(x ** 2) ... >>> res [1, 4, 9, 16, 25] # map版本 >>> map((lambda x: x ** 2), [1, 2, 3, 4, 5]) [1, 4, 9, 16, 25] >>> map(None, [1, 2, 3, 4, 5]) # function爲None,則默認爲恆等函數 [1, 2, 3, 4, 5] >>> map((lambda x, y: x + y), [1, 2, 3], [4, 5, 6]) # 存在多個iterable,則返回[1+4, 2+5, 3+6] [5, 7, 9] >>> map(None, [1, 2, 3], [4, 5]) # 以最長的iterable爲準,其餘不足的補None [(1, 4), (2, 5), (3, None)]
函數原型:reduce(function, iterable[, initializer])
說明:以累加方式逐個取出iterable中的元素做爲參數調用(具備雙參數的)function,從而最終將iterable簡化爲一個值(如reduce(function, [1, 2, 3])會返回function(function(1, 2), 3))。若是存在initializer,則在累加調用中,以它做爲初始的第一個參數。function必須是可調用對象(不能爲None)。
示例:
# for循環版本 >>> total = 0 >>> for x in [1, 2, 3, 4, 5]: ... total += x ... >>> total 15 # reduce版本 >>> reduce((lambda x, y: x + y), [1, 2, 3, 4, 5]) # 至關於((((1+2)+3)+4)+5) 15 >>> reduce((lambda x, y: x + y), [1, 2, 3, 4, 5], 10) # 帶有initializer的reduce,至關於(((((10+1)+2)+3)+4)+5) 25 >>> sum([1, 2, 3, 4, 5], 10) # 等效於上面的reduce 25
閉包(closure)是一個內嵌函數,它可以記住其 外圍做用域 中的全部名字,即便這個做用域 看起來 已經不在外圍。
在如下示例中,內嵌函數action就是一個閉包:
>>> def maker(N): ... def action(x): ... return x * N ... return action ... >>> mul10 = maker(10) >>> mul10(3) 30 >>> mul10(5) 50
儘管函數調用mul10 = maker(10)
已經返回並退出了,但後續的mul10卻可以記住整數10,從而計算入參的10倍數。
實際上,外圍做用域(如函數maker對應的代碼範圍)中的全部名字(如參數N)都做爲環境信息被綁定到了action函數上,所以每次調用action時均可以訪問這些環境信息。特別地,能夠經過特殊屬性func_closure
來獲取一個函數的自由變量綁定:
>>> def maker(N): ... def action(x): ... return x * N ... print(action.func_closure) # 打印出action函數的func_closure屬性值 ... return action ... >>> N = 10 >>> print('int N: id = %#0x, val = %d' % (id(N), N)) # N的值爲10(整數10的地址是0x8e82044) int N: id = 0x8e82044, val = 10 >>> mul10 = maker(N) # action.func_closure中含有整數10(即自由變量N) (<cell at 0x90e96bc: int object at 0x8e82044>,)
閉包的這種 可以記住環境狀態 的特性很是有用,Python中有一些其餘特性就是藉助閉包來實現的,好比 裝飾器。
偏函數應用(Partial Function Application)是一種簡化函數調用的方式,主要表現爲對函數的部分參數進行固化。
Python中的偏函數應用是藉助 functools.partial 來完成的。例若有一個專用於生成文章標題的函數title:
>>> def title(topic, part): ... return topic + u':' + part ...
若是要爲 『Python基礎』 系列的多篇文章生成標題,能夠有如下兩種方式:
# 普通版本 >>> print title(u'Python基礎', u'開篇') Python基礎:開篇 >>> print title(u'Python基礎', u'函數') Python基礎:函數 >>> print title(u'Python基礎', u'函數式編程') Python基礎:函數式編程 # 偏函數版本 >>> from functools import partial >>> pybasic_title = partial(title, u'Python基礎') >>> print pybasic_title(u'開篇') Python基礎:開篇 >>> print pybasic_title(u'函數') Python基礎:函數 >>> print pybasic_title(u'函數式編程') Python基礎:函數式編程
從上面的示例能夠看出,若是在編碼過程當中遇到了「屢次用相同的參數調用一個函數」的場景,就能夠考慮使用偏函數來固化這些相同的參數,進而簡化函數調用。
1)默認參數
在上述示例中,若是將函數title的定義改成def title(part, topic=u'Python基礎')
也能夠達到相同的效果。可是這種方式的不足之處也很明顯:
相比之下,偏函數具備很好的靈活性:既不用修改已有函數的定義,又能夠爲函數的參數固化不一樣的值。
2)lambda表達式
使用 lambda表達式 也能夠實現相似偏函數的功能,而且與默認參數不一樣的是,能夠針對不一樣的參數值定義不一樣的lambda表達式(由於lambda表達式一般是一次性使用的)。例如上述示例中的pybasic_title也能夠實現爲:
>>> pybasic_title = (lambda part: u'Python基礎:' + part) >>> print pybasic_title(u'開篇') Python基礎:開篇 >>> print pybasic_title(u'函數') Python基礎:函數 >>> print pybasic_title(u'函數式編程') Python基礎:函數式編程
可是,因爲lambda表達式自己的限制(參考 『lambda表達式』 一節),在具備複雜函數的場景中,還得使用偏函數。
3)閉包
最後,使用 閉包 一樣能夠等效地實現偏函數的功能,而且與lambda表達式不一樣的是,它沒有任何限制場景。仍是上面的例子:
>>> def title(topic): ... def topic_title(part): ... return topic + u':' + part ... return topic_title ... >>> pybasic_title = title(u'Python基礎') >>> print pybasic_title(u'開篇') Python基礎:開篇 >>> print pybasic_title(u'函數') Python基礎:函數 >>> print pybasic_title(u'函數式編程') Python基礎:函數式編程
能夠看出,這個閉包版本的惟一缺點是它須要對函數title進行從新定義(與默認參數的狀況有些相似)。
總而言之,若是須要對 已有函數 進行參數固化,偏函數是最佳選擇。
關於 列表解析(List Comprehensions),在 Python基礎:序列(列表、元組) 中有過簡單介紹。
這裏主要強調兩點:
1)filter()
列表解析能夠徹底代替filter():
[item for item in iterable if function(item)]
等價於filter(function, iterable)
[item for item in iterable if item]
等價於filter(None, iterable)
2)map()
在如下狀況中,列表解析能夠代替map():
[function(item) for item in iterable]
等價於map(function, iterable)
[item for item in iterable]
等價於map(None, iterable)
[function(*args) for args in zip(iter1, iter2, ...)]
等價於map(function, iter1, iter2, ...)
zip(iter1, iter2, ...)
等價於map(None, iter1, iter2, ...)
若是多個iterable具備不一樣的長度,那麼列表解析就沒法代替map()了。
生成器表達式(Generator Expressions)與列表解析在語法和功能方面都很是類似。兩者的根本差別是:生成器表達式返回一個 生成器,而列表解析返回一個列表。以下所示:
差別點 | 生成器表達式 | 列表解析 |
---|---|---|
表示方法 | (expr for item in iterable if cond_expr) | [expr for item in iterable if cond_expr] |
返回值 | 一個生成器 | 一個列表 |
與列表解析相比,生成器表達式具備 延遲計算(lazy evaluation)的特色,所以在使用內存上更有效。關於生成器表達式的實際案例,能夠參考 Python核心編程(第二版) 中的 『8.13』 一節:『生成器表達式』。