generator是指一個函式,能夠產生一個sequence,但每產生一個值後,就會傳回值並將函式保持在此狀態,直到下一次呼叫此函式


In [1]:
# 這是一個 infinite generator,用來產生Fibonacci數列
def fibonacci():
    i, j = 0, 1
    while True:
        yield j  # 當傳回 j 之後,函式會停留在這一行,直到下一次呼叫為止
        i, j = j, i + j

In [2]:
# 印出1~500的fibonacci sequence
for x in fibonacci():
    if x > 500: break
    print x,


1 1 2 3 5 8 13 21 34 55 89 144 233 377

itertools

這個library提供許多generator相關的函式。

  • imap(func, p, q): 傳回func(p[i], q[i])
  • ifilter(pred_func, seq): 傳回seq中使pred_func(x)==True的值。
    如果pred_func==None,則傳回seq[i]==True的值
  • ifilterfalse(pred_func, seq): 傳回seq中使pred_func(x)==False的值。
    如果pred_func==None,則傳回seq[i]==False的值
  • izip(p, q): 傳回tuple (p[i], q[i]),如果p,q長度不相等,會以短的為主
  • izip_longest(p, q): 傳回tuple (p[i], q[i]),如果p,q長度不相等,會以長的為主
  • compress(data, sel): 傳回data[i] if sel[i]
  • chain(p1, p2, ...): 傳回p1[0],...,p1[-1],p2[0],...,p2[-1],...
  • takewhile(pred, seq): 傳回seq[i]直到pred(seq[i])==False
  • dropwhile(pred, seq): 傳回sen[i],從使得pred(seq[i])==Falsei開始
  • islice(seq [,start] ,stop [,step]): 傳回seq[range(start, stop, step)]
  • groupby(seq [,key]): 用來產生iterator of iterators。
    第一次使用next(groupby(seq, key)),會傳回(key, iterator)的組合。
    這個iterator會依序傳回seq[i],直到key(seq[i])改變為止。
    例如key(seq)==[1,1,0,0,1],則會產生三個(1, it1), (0, it2), (1, it3),其中it1產生seq[0],seq[1]it3產生seq[4]
    如果沒指定key,則key==seq[i]

itertools也有提供現成的generator:

  • count(start [,step]): 從start, start+step, ...開始的infinite generator
  • cycle(seq): 循環產生seq[0],...,seq[-1],seq[0],...的infinite generator
  • repeat(elem [,n]): 產生nelem,如果n沒有指定會變成infinite generator

In [3]:
from itertools import *

In [4]:
a = [1, 5, 8, 3, 6, 9, 2, 4, 7]
b = [11, 12, 13, 14]

In [5]:
list(imap(pow, b, a))  # 傳回 11^1, 12^5, 13^8, 14^3


Out[5]:
[11, 248832, 815730721, 2744]

In [6]:
list(ifilter(lambda x: x % 2, a))  # 傳回所有奇數


Out[6]:
[1, 5, 3, 9, 7]

In [7]:
list(ifilterfalse(lambda x: x % 2, a))  # 傳回所有偶數


Out[7]:
[8, 6, 2, 4]

In [8]:
list(izip(a, b))  # 結合成tuple,以短的為主


Out[8]:
[(1, 11), (5, 12), (8, 13), (3, 14)]

In [9]:
list(izip_longest(a, b))  # 結合成tuple,以長的為主,空的地方填 None


Out[9]:
[(1, 11),
 (5, 12),
 (8, 13),
 (3, 14),
 (6, None),
 (9, None),
 (2, None),
 (4, None),
 (7, None)]

In [10]:
list(izip_longest(a, b, fillvalue=20))  # 結合成tuple,以長的為主,空的地方填 20


Out[10]:
[(1, 11),
 (5, 12),
 (8, 13),
 (3, 14),
 (6, 20),
 (9, 20),
 (2, 20),
 (4, 20),
 (7, 20)]

In [11]:
list(compress('HELLO', [0, 1, 1, 0, 1]))  # 根據 boolean selector 選擇元素


Out[11]:
['E', 'L', 'O']

In [12]:
list(takewhile(lambda x: x != 9, a))  # 當 pred_func 傳回 False 時停止


Out[12]:
[1, 5, 8, 3, 6]

In [13]:
list(dropwhile(lambda x: x != 9, a))  # 當 pred_func 傳回 False 時開始


Out[13]:
[9, 2, 4, 7]

In [14]:
list(islice(fibonacci(), 3, 10, 2))  # 取出第 3, 5, 7, 9 個Fibonacci數


Out[14]:
[3, 8, 21, 55]

In [15]:
for k, it in groupby([1, 1, 2, 2, 2, 3, 1, 1]):  # 如果groupby沒有指定key,則iterable裡每個值都是自己的key
    print list(it)


[1, 1]
[2, 2, 2]
[3]
[1, 1]

In [16]:
for k, it in groupby(a, lambda x: x == 9):  # 當key值改變時,會產生新的iterator
    print list(it)


[1, 5, 8, 3, 6]
[9]
[2, 4, 7]

排列組合相關的function:

  • product(p, q): 傳回所有(p[i],q[j])的組合,總數應該是len(p)*len(q)
  • permutations(p, [,r]): 傳回p中取r個元素所有可能的排列,如果不指定r則會用所有元素
  • combinations(p, r): 傳回p中取r個元素的所有組合
  • combinations_with_replacement(p, r): 傳回p中取r個元素的所有組合,但元素可重複出現

In [17]:
list(product([1,2], [3,4,5]))


Out[17]:
[(1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5)]

In [18]:
list(permutations([1, 2, 3]))


Out[18]:
[(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]

In [19]:
list(permutations([1, 2, 3], 2))


Out[19]:
[(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]

In [20]:
list(combinations([1, 2, 3], 2))


Out[20]:
[(1, 2), (1, 3), (2, 3)]

In [21]:
list(combinations_with_replacement([1, 2, 3], 2))


Out[21]:
[(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)]

Recipes

next(iterator [,default]) 的用途是取得iterator的下一個元素

  • 如果default有設任何值(例如None),當iterator清空時,會傳回default
  • 如果default沒有設,當iterator清空時,會產生StopIteration

In [22]:
it = (i for i in b)
print next(it), next(it), next(it), next(it), next(it)


11 12 13 14
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-22-51cf432da0e2> in <module>()
      1 it = (i for i in b)
----> 2 print next(it), next(it), next(it), next(it), next(it)

StopIteration: 

要消耗一個iterator的前n個元素,可以結合nextislice
islice(iterator, n, n)會取出iterator前n個元素並丟棄


In [23]:
iterator = izip(a, b)  # 內容是 (1, 11), (5, 12), (8, 13), (3, 14)
next(islice(iterator, 2, 2), None)  # (1, 11), (5, 12) 被丟掉了
list(iterator)


Out[23]:
[(8, 13), (3, 14)]

要消耗一個iterator的所有元素,可以用collections.deque,它會一直取iterator的元素直到清空為止。
為了減少記憶體用量,collections.deque的大小可設為0,則取出的元素就會直接丟棄。


In [24]:
from collections import deque
iterator = izip(a, b)
deque(iterator, maxlen=0)
list(iterator)


Out[24]:
[]

計算符合條件的元素個數,可以結合sumimap


In [25]:
iterator = permutations([1, 2, 3])
sum(imap(lambda x: x[1] == 2, iterator))  # 符合條件的是 (1, 2, 3), (3, 2, 1)


Out[25]:
2

用有限長度的list產生無限長度的generator,但後面都填None


In [26]:
chain(permutations([1, 2, 3]), repeat(None))


Out[26]:
<itertools.chain at 0x3b34be0>

list of list結合成一個list


In [27]:
# 注意: list層數只會少一層,例如下面的 [8, 9] 仍然存在
list(chain.from_iterable([[1, 3], [4, 5], [6, [8, 9]]]))


Out[27]:
[1, 3, 4, 5, 6, [8, 9]]

list變成每n個一組的tuple


In [28]:
args = [iter('ABCDEFG')] * 3  # args是一個list of iterators [it, it, it],但三個it都代表同一個iterator
list(izip_longest(*args))  # 用 *args 將list展開成三個參數


Out[28]:
[('A', 'B', 'C'), ('D', 'E', 'F'), ('G', None, None)]

列出所有組合 (powerset)


In [29]:
list(chain.from_iterable(combinations([1, 2, 3], r) for r in range(4)))


Out[29]:
[(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]

In [ ]: