python 標準庫中提供了 itertools, functools, operator 三個庫支持函數式編程,對高階函數的支持,python 提供 decorator 語法糖。 迭代器 (iterator)和生成器(generator)概念是 python 函數式編程的基礎,利用迭代器和生成器能夠實現函數式編程中常常用到的 map(), filter(), reduce() 等過程以及 itertools, functools 中提供的絕大部分功能。html
迭代器和生成器依賴於 next(), iter() 方法和 yield 表達式java
next(iterator[, default]) 是內置的函數,經過調用 __next__() 方法取得 iterator 的下一個元素,全部元素消耗完再調用就會引發 StopIteration 異常。若是提供了 default 參數,則當取完全部元素後,再調用 next 時會返回 default 值,而不是引發 StopIteration 異常。python
iter(object[, sentinel]) 內置函數會返回一個迭代器。沒有第2個參數時, object 必須支持迭代協議(__iter__() 方法) 或序列協議 (__getitem__() 方法),不然會引發 TypeError 異常。若是有哨兵 (sentinel)參數, object 必須是可調用的對象,這種方式建立的迭代器每次調用 __next__() 方法時會以無參的形式調用 object ,若是返回值等於哨兵就會引發 StopIteration 異常,不然就返回這個值git
with open('mydata.txt') as fp: for line in iter(fp.readline, ''): process_line(line)
yield 英文意思是生產,是 python 的關鍵字,在函數返回值時用來替換 return 產生一個值。yield 表達式只能用於定義生成器函數中,在函數外使用 yield 會致使 SyntaxError: 'yield' outside function 。express
生成器控制生成器函數的執行,當調用生成器的某個函數時,開始執行,遇到第一個 yield 表達式時,返回 yield 後面表達式的值,而後被掛起(suspend),掛起時保持全部的局部狀態,包括局部變量綁定、指令指針、內部的求值棧、異常處理狀態;當再次調用生成器的某個方法時,執行流會恢復。編程
因此生成器函數很是像協程(coroutine,其它語言中的概念),二者都會 yield 屢次,有多個入口,執行流會被掛起。惟一的區別是生成器函數不能控制 yield 以後,執行流應該從哪繼續,控制老是被轉移到生成器的調用者,因此又被稱爲半協程(semicoroutine)。app
迭代器(iterator)必須至少要定義 __iter__() 和 __next__() 兩個方法,經過 iter() 和 next() 函數調用。 iter() 生成一個迭代器, next() 每調用一次都會返回下一個值,若是已經到最後一個值了,那麼再調用 next() 就會引發 StopIteration 異常。編程語言
#python的迭代器類須要實現__iter__魔法方法返回迭代器實例,還須要實現一個next方法,在迭代到末尾時拋出StopIteration異常表示迭代結束。以下簡單示例: class SimpleIterator: def __init__(self, maxvalue): self.current = 0 self.max = maxvalue def next(self): result = self.current self.current += 1 if result == self.max: raise StopIteration() return result def __iter__(self): return self li = list(SimpleIterator(5)) print li ################################ class Reverse: """Iterator for looping over a sequence backwards.""" def __init__(self, data): self.data = data self.index = len(data) def __iter__(self): return self def __next__(self): if self.index == 0: raise StopIteration self.index = self.index - 1 return self.data[self.index]
for 循環實際上就是先將 iter() 做用到容器對象上生成迭代器,而後每次調用 next() ,當引發 StopIteration 時就終止 for 循環。ide
for element in ['a', 'b', 'c']: print(element) # 等價於: it = iter(['a', 'b', 'c']) try: while True: print(next(it)) except StopIteration: pass
生成器其實就是一種特殊的迭代器,它使一種更爲高級、更爲優雅的迭代器,使用生成器讓咱們能夠以一種更加簡潔的語法來定義迭代器,它與普通函數相同,只是返回值時用 yield 而不是 return,局部變量和執行狀態在調用之間會自動保存。
讓咱們先明確如下兩點:函數式編程
def reverse(data): for index in range(len(data)-1, -1, -1): yield data[index] ################################# def yield_test(maxvalue): i = -1 while i < maxvalue-1: i += 1 yield i for i in yield_test(10): print i
能用 generator(函數) 實現的均可以用 iterator(類)實現,不過生成器 會自動建立 __iter__() 和 __next__() 方法,終止時也會自動引發 StopIteration 異常,於是顯得更緊湊。
利用生成器表達式(generator expression)能夠不用專門定義一個 generator 函數,直接就地使用。生成器表達式與列表表達式(list comprehension)相似,只不過用的是圓括號而不是方括號,因爲生成器只保存上次執行的狀態,因此相比列表表達式,生成器表達式佔用內存更少。
生成器本質上至關於函數式編程語言中的流的概念,流表面上看是一個序列,但這個序列不是一次構造出來的,而是在須要時構建,函數式編程語言中流是經過惰性求值實現的,能夠看到 python 是經過關鍵詞 yield 實現的。
使用流的概念能夠避免命令式程序設計中賦值帶來的反作用,同時更加簡潔優雅。用序列模擬時間變化,至關因而座標變換,當咱們觀察一個正在移動的粒子時,咱們說該粒子的位置(狀態)正在變化,而從粒子的世界線的觀點看,這裏就根本不涉及任何變化。
TODO : 補充例子
python3 將 python2 中許多列表改爲了迭代器,更加函數式了,例如 range(), zip() 在 python2 中返回列表,而 python3 中返回一個迭代器,因爲迭代器只是在須要(next())時取元素而不是一次就構建整個列表,因此能夠表示很是大的序列甚至無窮序列。
生成器——迭代器方法 能夠用來控制生成器函數的執行流
__next__()
send(value) 恢復執行流,並將 value 發送到生成器函數,value 做爲當前 yield 表達式的值
throw(type[, value[, traceback]]) 在生成器暫停的地方引發 type 類型的異常,並返回生成器函數產生的下一個值
close
>>> def echo(value=None): ... print("Execution starts when 'next()' is called for the first time.") ... try: ... while True: ... try: ... value = (yield value) ... except Exception as e: ... value = e ... finally: ... print("Don't forget to clean up when 'close()' is called.") ... >>> generator = echo(1) >>> print(next(generator)) Execution starts when 'next()' is called for the first time. 1 >>> print(next(generator)) None >>> print(generator.send(2)) 2 >>> generator.throw(TypeError, "spam") TypeError('spam',) >>> generator.close() Don't forget to clean up when 'close()' is called.
有了上面迭代器和生成器,就能夠實現各類函數式編程了,下面是函數式編程中經常使用的幾個函數,更多例子能夠查看 itertools 文檔
當 map(function, iterable,...) 接收 n 個 iterable 時,每次在各 iterable 中各取一個元素傳給 function 做參數,因此 function 必須可以接收 n 個參數,當各個 iterable 長度不同時按最短的終止,例如 map(lambda x,y: x+y, [1,2], [3,4], [5,6]) 會報錯, map(lambda x,y: x+y, 'abcd', 'def') 返回的迭代器依次爲 'ad', 'be', 'cf'
# 這個實現很差,用到了 zip,不過 zip 也能夠經過生成器實現(見後面) def map(function, *iterables): for args in zip(*iterables): yield function(*args)
itertools.starmap(function, iterable) 只接收一個 iterable,當 function 接收多個參數時,各個參數是放在元組中的,例如 itertools.starmap(pow, [(2,5), (3,2), (10,3)]) 返回迭代器的值依次爲 32, 9, 1000。
def starmap(function, iterable): for args in iterable: yield function(*args)
filter(function, iterable) 函數至關於生成器表達式 (item for item in iterable if function(item)) ,沒有提供 function 參數時至關於 (item for item in iterable if item)
itertools 中提供 filterfalse(predicate, iterable) 函數, filterfalse(lambda x: x%2, range(10)) 獲得 0,2,4,6,8, 的迭代器
def filterfalse(predicate, iterable): if predicate i None: predicate = bool for x in iterable: if not predicate(x): yield x
reduce(function, iterable[, initializer]) 函數將 function 從左到右兩個兩個地累計做用到 iterable 上,從而將 iterable 歸約到一個值,例如 reduce(lambda x, y: x+y, [1,2,3,4]) 會計算 (((1+2)+3)+4),從而獲得10。 python3 已經將內置的 reduce 函數移到 functools 模塊中了
def reduce(function, iterable, initializer=None): it = iter(iterable) if initializer is None: value = next(it) else: value = initializer for element in it: value = function(value, element) return value
enumerate(iterable, start=0) 生成一個枚舉迭代器,每次調用 next() 時會返回一個元組,包含計數(從 start 開始)和值(iterable)
seasons = ['Spring', 'Summer', 'Fall', 'Winter'] list(enumerate(seasons)) # => [(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')] list(enumerate(seasons, start=1)) # => [(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]
能夠利用生成器實現 enumerate
def enumerate(sequence, start=0): n = start for elem in sequence: yield n, elem n += 1
zip(*iterables) 返回元組迭代器,iterables 長度不一樣時,按最短的截斷, itertools 模塊中有 zip_longest() 函數。
a = [1, 2, 3] b = [1, 4, 9] c = [1, 8, 27] list(zip(a, b, c)) # => [(1,1,1), (2,4,8), (3,9,27)]
利用生成器實現 zip
def zip(*iterables): sentinel = object() iterators = [iter(it) for it in iterables] while iterators: result = [] for it in iterators: elem = next(it, sentinel) if elem in sentinel: return result.append(elem) yield tuple(result)
標準庫 itertools 提供 accumulate(iterable[,func]) 函數,將 func 函數做用到 iterable 相鄰元素上,累計起來,返回的也是一個迭代器。例如 accumulate([1,2,3,4,5]) 返回迭代器,其值依次爲 1, 3, 6, 10, 15,而 accumulate([1, 2, 3, 4, 5], operator.mul) 則返回迭代器的值依次爲 1, 2, 6, 24, 120
一樣也能夠用生成器實現 accumulate
def accumulate(iterable, func=operator.add): it = iter(iterable) total = next(it) yield total for element in it: total = func(total, element) yield total
itertools.cycle(iterable) 將 iterable 串起來做爲 iterator 返回,是無窮循環。例如 cycle('ABCD') 返回迭代器,其值是 A B C D A B C D A …
利用生成器實現 cycle
def cycle(iterable): saved = [] for element in iterable: yield element saved.append(element) while saved: for element in saved: yield element
利用迭代器實現 groupby
class groupby: # [k for k, g in groupby('AAAABBBCCDAABBB')] --> A B C D A B # [list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D def __init__(self, iterable, key=None): if key is None: key = lambda x: x self.keyfunc = key self.it = iter(iterable) self.tgtkey = self.currkey = self.currvalue = object() def __iter__(self): return self def __next__(self): while self.currkey == self.tgtkey: self.currvalue = next(self.it) # Exit on StopIteration self.currkey = self.keyfunc(self.currvalue) self.tgtkey = self.currkey return (self.currkey, self._grouper(self.tgtkey)) def _grouper(self, tgtkey): while self.currkey == tgtkey: yield self.currvalue self.currvalue = next(self.it) # Exit on StopIteration self.currkey = self.keyfunc(self.currvalue)
[1] Python 函數式編程
http://dengshuan.me/techs/python-functional.html
[2] Java FP: Java中函數式編程的謂詞函數(Predicates)第一部分
http://ifeve.com/functional-style-in-java-with/
[3] itertools — 建立高效迭代器的函數
http://python.usyiyi.cn/python_278/library/itertools.html
[4] itertools — Functions creating iterators for efficient looping (高效循環迭代器建立函數)
http://data.digitser.net/python_3.4.2/zh-CN/library/itertools.html
[5] PYTHON-進階-ITERTOOLS模塊小結
http://wklken.me/posts/2013/08/20/python-extra-itertools.html
[6] (譯)Python關鍵字yield的解釋(stackoverflow)
http://pyzh.readthedocs.org/en/latest/the-python-yield-keyword-explained.html
[7] Python yield 使用淺析
http://my.oschina.net/leejun2005/blog/94175
[8] Python函數式編程指南(三):迭代器
http://www.cnblogs.com/huxi/archive/2011/07/01/2095931.html
[9] Python函數式編程指南(四):生成器
http://www.cnblogs.com/huxi/archive/2011/07/14/2106863.html
[10] 可迭代對象 vs 迭代器 vs 生成器