可迭代器(iterable),不只限於list/str等,還包括任何包含有yield關鍵字的函數,後者未必有規律的迭代特徵。標準庫中的itertools包提供了更加靈活的產生迭代器的工具,這些工具的輸入大都是已有的迭代器函數的封裝,而且itertools給出的函數都是針對廣義迭代器而言。而len()等函數是針對狹義迭代器,即sequence(i.e. str, list, tuple)而言的。html
之內置函數range()爲例,執行結果會是一次性計算好整個序列。這對於很長的序列來講會比較耗時,甚至帶來性能問題。於是,python還提供了內置函數,提供了惰性求值版本,那就是xrange()。它利用yield特性,第一次執行時僅僅返回迭代器,不到用時是不會求值的。java
實際上,itertools提供的函數都是惰性的,而且給原內置函數都重寫了惰性版本。如imap()對於內置的map()。python
擴展庫Pipe則對內置函數和部分itertools進行了封裝,提供了相似unix bash下的管道式調用風格,更接近人類從左到右的閱讀習慣,使得代碼更加優雅。其餘動態語言,如ruby, c#-lambda java8-lambda也都提供了相似的鏈式調用形式。編程
另外,也提供了@Pipe裝飾器,能夠很是方便地擴展出本身的管道函數,或者繼續封裝其餘itertools中的有用函數。c#
這裏是Pipe官方給出的例子,用管道函數式編程解出 https://projecteuler.net/ 中的三道題目:ruby
# Find the sum of all the multiples of 3 or 5 below 1000. euler1 = (itertools.count() | select(lambda x: x * 3) | take_while(lambda x: x < 1000) | add) \ + (itertools.count() | select(lambda x: x * 5) | take_while(lambda x: x < 1000) | add) \ - (itertools.count() | select(lambda x: x * 15) | take_while(lambda x: x < 1000) | add) assert euler1 == 233168 # Find the sum of all the even-valued terms in Fibonacci which do not exceed four million. euler2 = fib() | where(lambda x: x % 2 == 0) | take_while(lambda x: x < 4000000) | add assert euler2 == 4613732 # Find the difference between the sum of the squares of the first one hundred natural numbers and the square of the sum. square = lambda x: x * x euler6 = square(itertools.count(1) | take(100) | add) - (itertools.count(1) | take(100) | select(square) | add) assert euler6 == 25164150
注意:全部惰性求值的迭代器,都是隻能求值一次的,若是再次求值會什麼也得不到,由於yield堆棧已經走到底,沒法回頭。所以,當要對惰性迭代器重複使用時,必須故意地提早將其求值展開,或者利用itertools.tee來克隆一個迭代器。bash
"42" | stdout # 輸出到標準輸出 >>> 42 (0, 1, 2) | as_list # 求值並轉換list類型並輸出 >>> [0, 1, 2] lineout # 行輸出到標準輸出 tee # 輸出迭代器的每一個元素,經常使用於debug。不一樣於itertools.tee! as_tuple # 轉換tuple類型並輸出 as_dict # 轉換dict類型並輸出 netcat # 發送網絡請求並讀取響應 netwrite # 發送網絡請求但不讀取響應
[1, 2, 3, 4] | concat("#") # 鏈接字符串 >>>'1#2#3#4' average # 求平均 count # 求長度或計數。相似len,但適用於全部迭代器 add # 求和,同sum max # 求最大值,同max min # 求最小值,同min any # 只要有一個元素知足條件就返回真,相似any,但適用於全部迭代器 all # 若是全部元素都知足條件才返回真,相似all,但適用於全部迭代器
(1, 2, 3, 4, 5, 6, 7, 8, 9) \ | groupby(lambda x: x % 2 and "Even" or "Odd") | select(lambda x: "%s : %s" % (x[0], (x[1] | concat(', ')))) | concat(' / ') # 將key函數做用於原循環器的各個元素, 根據key函數結果,將擁有相同函數結果的元素分到一個新的迭代器。以key函數返回結果爲鍵, 以新迭代器爲值的鍵值對序列。封裝了itertools.groupby >>> 'Even : 1, 3, 5, 7, 9 / Odd : 2, 4, 6, 8' [[1, 2], [3, 4], [5]] | chain | concat # 封裝了itertools.chain, 將兩個可迭代器展開鏈接成爲一個。 >>> '1, 2, 3, 4, 5' (1, 2, 3) | chain_with([4, 5], [6]) | concat # 封裝了itertools.chain, 將兩個可迭代器展開鏈接成爲一個。>>> 1, 2, 3, 4, 5, 6 [[1, 2], [[[3], [[4]]], [5]]] | traverse | concat # 遞歸地展開迭代器並鏈接成一個 >>> '1, 2, 3, 4, 5' 'abc' | permutations(2) | as_list # 排列組合之排列,封裝了itertools.permutations, 從'abc'中挑選2元素,好比ab, ba, bc, ... (ab, ba都包含)將全部結果排序,返回爲新的迭代器。 itertools.combinations('abc', 2) # 排列組合之組合, 從'abcd'中挑選兩個元素,好比ab, bc, ... 將全部結果排序,返回爲新的迭代器。 itertools.combinations_with_replacement('abc', 2) # 排列組合之組合,但容許兩次選出的元素重複。即多了aa, bb, cc itertools.product('abc', [1, 2]) | as_list # 多個迭代器的笛卡爾積。至關於嵌套循環 >>> [('a', 1), ('a', 2), ('b', 1), ('b', 2), ('c', 1), ('c', 2)]
itertools.count(5, 2) # 從5開始的整數循環器,每次增長2,即5, 7, 9, 11, 13, 15 ...步長默認爲1 itertools.cycle('abc') # 重複序列的元素,即a, b, c, a, b, c ... itertools.repeat(10, 5) # 重複元素10,共重複5次,即10,10,10,10,10。不寫次數的話,默認無窮循環。
xrange(5) | aggregate(lambda x, y: x + y, initializer=0) # 同內置reduce函數 >>> 10 [5, -4, 3, -2, 1] | sort(key=abs) | concat # 排序, 同sorted。>>> '1, -2, 3, -4, 5' [1, 2, 3] | select(lambda x: x * x) | concat # 做用同map/imap, >>> '1, 4, 9' [1, 2, 3] | where(lambda x: x % 2 == 0) | concat # 做用同filter/ifilter >>> '2' [1, 2, 3, 4] | take_while(lambda x: x < 3) | concat # 不斷取出元素,直到運算爲假,封裝了itertools.takewhile, >>> '1, 2' [1, 2, 3, 4] | skip_while(lambda x: x < 3) | concat # 不斷跳過元素,直到運算爲假,封裝了itertools.dropwhile >>> '3, 4' (1, 2, 3, 4, 5, 6, 7, 8, 9) \ | izip([9, 8, 7, 6, 5, 4, 3, 2, 1]) \ | concat # 封裝了zip的惰性版本,即itertools.izip,縱向合併兩個序列 >>> '(1, 9), (2, 8), (3, 7), (4, 6), (5, 5), (6, 4), (7, 3), (8, 2), (9, 1)' (1, 2, 3, 4, 5, 6, 7, 8, 9) | islice(2, 8, 2) | concat # 封裝了slice的惰性版本,即itertools.islice,切片器 >>> '3, 5, 7' itertools.compress('ABCD', [1, 1, 1, 0]) # 根據[1, 1, 1, 0]的真假值狀況,選擇第一個參數'ABCD'中的元素。 >>> A, B, C take # 取出n個元素 skip # 跳過n個元素 first # 取出第一個元素 tail # 取出倒數n個元素 reverse # 逆序,同reversed itertools.tee # 克隆出n個相同的迭代器
itertools.imap(pow, (1,2,3),(1,2,3)) | as_list # map的惰性版本 >>> [1, 4, 27] itertools.starmap(pow, ((2,3),(2,3))) | as_list # map的惰性版本,可是對sequence中每一個元組來執行函數 >>> [8, 8] itertools.ifilter # filter的惰性版本 itertools.ifilterfalse # filter的惰性反值版本 itertools.izip # zip的惰性版本 itertools.izip_longest # slice的惰性版本
from pipe import * select = Pipe(lambda iterable, pred: (pred(x) for x in iterable)) # OR @Pipe def first(iterable, default=None): try: return next(iter(iterable)) except StopIteration: return default
[1] 函數式Python中的Pipe與itertools網絡
http://www.jackyshen.com/2015/08/31/pipe-and-itertools-in-functional-python/
異步
[2] Python標準庫13 循環器 (itertools)async
http://www.cnblogs.com/vamei/p/3174796.html
[3] Python:itertools模塊
http://www.cnblogs.com/cython/articles/2169009.html
[4] 利用python yielding建立協程將異步編程同步化