Python3標準庫built-in、itertools、functools中的生成器

介紹

Python3中實現了不少生成器函數,本篇主要介紹built-in、itertools、functools模塊中的生成器。python

過濾器生成器

本類生成器函數將iterable對象做爲參數,在不改變該iterable對象的條件下,返回iterable子集的生成器對象。segmentfault

filter(predicate, iterable)

iterable的每個元素會傳入predicate函數中判斷是否爲True,該生成器會返回全部返回爲True的元素組成的生成器對象。異步

def is_vowel(c):
    return c.lower() in 'aeiou'
    
word = 'abcdefghijk'
print(list(filter(is_vowel, word)))
## output: ['a', 'e', 'i']

filter會過濾掉word中全部非元音字母,返回符合元素組成的生成器對象。
注意:經過list(generator)能夠將生成器對象轉換爲列表,但若是是無限生成器list將會產生大量元素致使出錯。
filter函數等同於下面的生成器表達式用法。函數

(item for item in iterable if function(item))

若是filter的第一個參數爲None,則不過濾返回所有,等同於下面的生成器表達式用法。工具

(item for item in iterable if item)

itertools.filterfalse(predicate, iterable)

該函數和filter相似,區別是過濾掉predicate返回True的元素。ui

print(list(itertools.filterfalse(is_vowel, word)))
## output: ['b', 'c', 'd', 'f', 'g', 'h', 'j', 'k']

itertools.takewhile(predicate, iterable)

該函數連續迭代iterable對象中的元素,並用predicate函數判斷,若predicate返回爲True,將不斷產出該元素,直到predicate返回False,過濾了iterable後面不符合的元素。code

print(list(itertools.takewhile(is_vowel, word)))
## output: ['a']

itertools.dropwhile(predicate, iterable)

該函數與itertools.takewhile相反,過濾了iterable對象前面符合predicate返回True的元素,保留後面的子集。對象

print(list(itertools.dropwhile(is_vowel, word)))
## output: ['b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']

itertools.compress(iterable, selectors)

該函數中的selectors也是一個迭代對象,compress根絕selectors中的值(0/1或是True/False)判斷是否過濾iterable中的元素。排序

print(list(itertools.compress(word, [1, 0, 1, 0])))
## output: ['a', 'c']

若是selectors長度不夠,則iterable後面的對象所有被過濾掉。索引

itertools.islice(iterable, stop)

根據傳入參數的個數不一樣,該函數另外一種寫法是itertools.islice(iterable, start, stop[, step]),islice函數相似python中的分片操做:list[start:stop:step]。

print(list(itertools.islice(word, 4)))
## output: ['a', 'b', 'c', 'd']
print(list(itertools.islice(word, 4, 8)))
## output: ['e', 'f', 'g', 'h']
print(list(itertools.islice(word, 4, 8, 2)))
## output: ['e', 'g']

映射生成器

該類生成器主要對於傳入的一個或多個迭代對象中的每個元素進行操做,返回映射後的生成器對象。

map(func, *iterables, timeout=None, chunksize=1)

map是Python中經常使用的原生生成器,將迭代對象中的每個元素傳入func進行映射返回新的迭代對象。若是有n個iterable對象,則func的參數則爲n個,後面的timeout和chunksize參數涉及到異步,本篇將不闡述。

print(list(map(lambda x: x.upper(), word)))
print([x.upper() for x in word])
## output: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K']

上面第一行中的map將word中的每一個元素轉換爲大寫,和第二行中的列表生成式用法類似。

print(list(map(lambda x, y: (x, y), word, word)))
print(list(zip(word, word)))
## output: [('a', 'a'), ('b', 'b'), ('c', 'c') ... ('k', 'k')]

當有兩個iterable傳入時,func將須要處理傳入的兩個參數,第一行的用法和zip函數的做用類似。

itertools.starmap(function, iterable)

當iterable中的元素也是個迭代對象時,若是使用map函數,須要在函數內部實現解壓操做獲取到單個元素,而startmap將iterable中的元素按function(*item)方式傳入,咱們能夠在定義function的參數時完成解壓操做。舉例,若是想輸入序列[(2,5), (3,2), (10,3)]來獲得一個每一個元組元素的和的序列[7, 5, 13], 若使用map方法,fun函數將會複雜,而使用startmap則只須要傳遞一個add函數做爲startmap參數,元組解壓後的兩個值將傳入add函數做爲參數。

from operator import add
print(list(map(lambda x: add(x[0], x[1]), [(2, 5), (3, 2), (10, 3)])))
print(list(itertools.starmap(add, [(2, 5), (3, 2), (10, 3)])))
## output: [7, 5, 13]

enumerate(iterable, start=0)

enumerate函數也是常見的生成器函數,它的主要用法是提供for-in循環中的索引。若設置start參數,索引將從start值開始逐1增長。

for i, c in enumerate(word, 2):
    print(i, c)

itertools.accumulate(iterable[, func])

accumulate函數將經過func函數完成逐步累加操做,默認func爲operator.add。下面用例子進行說明。

sample = [1, 2, 3, 4, 5]
print(list(itertools.accumulate(sample)))
## output: [1, 3, 6, 10, 15]
print(list(itertools.accumulate(sample, mul)))
## output: [1, 2, 6, 24, 120]
print(list(itertools.accumulate(sample, mul)))
## output: [1, 2, 6, 24, 120]
print(list(itertools.accumulate(sample, min)))
## output: [1, 1, 1, 1, 1]
print(list(itertools.accumulate(sample, max)))
## output: [1, 2, 3, 4, 5]
print(list(itertools.starmap(lambda x, y: y/x, 
                             enumerate(itertools.accumulate(sample), 1))))
## output: [1.0, 1.5, 2.0, 2.5, 3.0]

合併生成器

合併生成器接收多個可迭代對象參數,將他們組合後返回新的生成器對象。

itertools.chain(*iterables)

chain生成器函數接收多個可迭代對象參數,將他們按順序組合成新的生成器對象返回。

print(list(itertools.chain(range(3), range(3, 7))))
## output: [0, 1, 2, 3, 4, 5, 6]

itertools.chain.from_iterable(iterable)

chain.from_iterable函數接收一個元素爲可迭對象的可迭代對象,將該全部可迭代的元素拆開,從新按順序組合成一個新的生成器,新的生成器產出的元素爲iterable參數某個元素的解壓,chain.from_iterable功能更像是逐層解壓迭代對象。

a, b = [1,2], [3,4]
iterable= [[a,b],[a,b]]
print(iterable)
new_iterable = list(itertools.chain.from_iterable(iterable))
print(new_iterable)
print(list(itertools.chain.from_iterable(new_iterable)))
## output:
## [[[1, 2], [3, 4]], [[1, 2], [3, 4]]]
## [[1, 2], [3, 4], [1, 2], [3, 4]]
## [1, 2, 3, 4, 1, 2, 3, 4]

zip(*iterables)

zip函數接收多個iterable參數,並提取每一個iterable元素組成元組,返回這些元組組成的生成器對象。

iterable1 = 'abcd'
iterable2 = [1, 2, 3]
iterable3 = [10, 20, 30, 40]
print(list(zip(iterable1, iterable2, iterable3)))
## output:
## [('a', 1, 10), ('b', 2, 20), ('c', 3, 30)]

若是多個iterable元素個數不一致,zip會在最短的iterable耗盡後中止。
咱們能夠經過zip函數生成一個字典對象

keys = 'abc'
values = [1, 2, 3]
print(dict(zip(keys, values)))
## output: {'a': 1, 'b': 2, 'c': 3}

itertools.zip_longest(*iterables, fillvalue=None)

zip_longes函數做用和zip相似,在zip中若是某個iterable對象耗盡,生成器將就此中止,而zip_longest函數將爲耗盡的iterable補充fillvalue值。

iterable1 = 'abcd'
iterable2 = [1, 2, 3]
iterable3 = [10, 20, 30, 40]
print(list(itertools.zip_longest(iterable1, iterable2, iterable3, fillvalue=0)))
## output: [('a', 1, 10), ('b', 2, 20), ('c', 3, 30), ('d', 0, 40)]

itertools.product(*iterables, repeat=1)

product函數計算全部iterable的笛卡爾積,它像是生成器表達式中處理嵌套循環的步驟,product(a, b)能夠等同於((x, y) for x in a for y in b)。
repeat至關於擴展了iterables, product(a, b, repeat=2)至關於product(a, b, a, b)

a = (0, 1)
b = (2, 3)
print(list(itertools.product(a, b)))
print(list(itertools.product(a, repeat=2)))

## output:
## [(0, 2), (0, 3), (1, 2), (1, 3)]
## [(0, 0), (0, 1), (1, 0), (1, 1)]

擴展生成器

擴展生成器將傳進的單一對象進行擴展,生成更多元素組成的生成器對象。

itertools.repeat(object[, times])

repeat函數能夠接收一個對象(能夠不是可迭代對象), 根據非必選參數times,生成元素個數爲times的生成器,若是不提供times參數,將生成無限生成器。

print(list(itertools.repeat(1, 3)))
print(list(itertools.repeat((1, 2), 3)))
print(list(zip(range(1, 4), itertools.repeat('a'))))
print([1, 2] * 3)

"""output:
[1, 1, 1]
[(1, 2), (1, 2), (1, 2)]
[(1, 'a'), (2, 'a'), (3, 'a')]
[1, 2, 1, 2, 1, 2]
"""

注意repeat()和列表乘法的區別,經過上文提到的itertools.chain.from_iterable函數結合repeat函數能夠實現列表乘法。

lst = [1, 2, 3]
g = itertools.repeat(lst, 3)
print(list(itertools.chain.from_iterable(g)))
print(lst * 3)
"""output
[1, 2, 3, 1, 2, 3, 1, 2, 3]
[1, 2, 3, 1, 2, 3, 1, 2, 3]
"""

itertools.cycle(iterable)

cycle函數將傳進的iterable可迭代對象首尾相連造成循環,生成無限生成器。

# cycle('ABCD') --> A B C D A B C D A B C D ...

itertools.count(start=0, step=1)

計數器函數,start和step參數能夠爲小數,直接看例子。

g = itertools.count(1.2, 2.5)
print(next(g))
print(next(g))
print(next(g))
"""output:
1.2
3.7
6.2
"""

上文提到的enumerate生成器函數能夠經過map和count來實現。

for i, v in map(lambda x, y: (x, y), itertools.count(), range(3, 10)):
    print(i, v)

咱們能夠經過調整count函數讓索引i的值更加靈活。
Python中的range(start, stop[, step])函數能夠生成一個序列,可是要求輸入參數必須爲整數,能夠經過count函數實現一個能夠接收小數的新range。

def range_new(start, stop, step):
    for i in itertools.count(start, step):
        if i >= stop:
            break
        yield i

print(list(range_new(1, 5.5, 1.5)))
## output: [1, 2.5, 4.0]

排列組合生成器

如下三個函數能夠實現迭代對象的排列組合
itertools.combinations(iterable, r)
非重複組合

print(list(itertools.combinations('ABC', 1)))
print(list(itertools.combinations('ABC', 2)))
print(list(itertools.combinations('ABC', 3)))
"""output:
[('A',), ('B',), ('C',)]
[('A', 'B'), ('A', 'C'), ('B', 'C')]
[('A', 'B', 'C')]
"""

itertools.combinations_with_replacement(iterable, r)
重複組合

print(list(itertools.combinations_with_replacement('ABC', 1)))
print(list(itertools.combinations_with_replacement('ABC', 2)))
print(list(itertools.combinations_with_replacement('ABC', 3)))
"""output:
[('A',), ('B',), ('C',)]
[('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'B'), ('B', 'C'), ('C', 'C')]
[('A', 'A', 'A'), ('A', 'A', 'B'), ('A', 'A', 'C'), ('A', 'B', 'B'), ('A', 'B', 'C'), ('A', 'C', 'C'), ('B', 'B', 'B'), ('B', 'B', 'C'), ('B', 'C', 'C'), ('C', 'C', 'C')]
"""

itertools.permutations(iterable, r=None)
全排列

print(list(itertools.permutations('ABC', 1)))
print(list(itertools.permutations('ABC', 2)))
print(list(itertools.permutations('ABC', 3)))
"""output:
[('A',), ('B',), ('C',)]
[('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')]
[('A', 'B', 'C'), ('A', 'C', 'B'), ('B', 'A', 'C'), ('B', 'C', 'A'), ('C', 'A', 'B'), ('C', 'B', 'A')]
"""

對比itertools.product(*iterables, repeat=1)函數

print(list(itertools.product('ABC', repeat=1)))
print(list(itertools.product('ABC', repeat=2)))
"""output:
[('A',), ('B',), ('C',)]
[('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'B'), ('B', 'C'), ('C', 'A'), ('C', 'B'), ('C', 'C')]
"""

整理生成器

此類生成器將傳入的可迭代對象通過整理後,以生成器的形式所有返回。

itertools.groupby(iterable, key=None)

groupby生成器能夠根據key,將iterable分組,返回的生成器的元素爲(key, iterable)的元組形式。掃描整個序列而且查找連續相同值(或者根據指定 key 函數返回值相同)的元素序列。 在每次迭代的時候,它會返回一個值和一個迭代器對象, 這個迭代器對象能夠生成元素值所有等於上面那個值的組中全部對象。

g = itertools.groupby('LLLLAAGGG')
for char, group in g:
    print(char, '->', list(group))

"""output:
L -> ['L', 'L', 'L', 'L']
A -> ['A', 'A']
G -> ['G', 'G', 'G']
"""

rows = [
    {'address': '5412 N CLARK', 'date': '07/01/2012'},
    {'address': '5148 N CLARK', 'date': '07/04/2012'},
    {'address': '5800 E 58TH', 'date': '07/02/2012'},
    {'address': '2122 N CLARK', 'date': '07/03/2012'},
    {'address': '5645 N RAVENSWOOD', 'date': '07/02/2012'},
    {'address': '1060 W ADDISON', 'date': '07/02/2012'},
    {'address': '4801 N BROADWAY', 'date': '07/01/2012'},
    {'address': '1039 W GRANVILLE', 'date': '07/04/2012'},
]

rows.sort(key=itemgetter('date'))
g = itertools.groupby(rows, itemgetter('date'))
for char, group in g:
    print(char, '->', list(group))
"""output:
07/01/2012 -> [{'address': '5412 N CLARK', 'date': '07/01/2012'}, {'address': '4801 N BROADWAY', 'date': '07/01/2012'}]
07/02/2012 -> [{'address': '5800 E 58TH', 'date': '07/02/2012'}, {'address': '5645 N RAVENSWOOD', 'date': '07/02/2012'}, {'address': '1060 W ADDISON', 'date': '07/02/2012'}]
07/03/2012 -> [{'address': '2122 N CLARK', 'date': '07/03/2012'}]
07/04/2012 -> [{'address': '5148 N CLARK', 'date': '07/04/2012'}, {'address': '1039 W GRANVILLE', 'date': '07/04/2012'}]
"""

groupby() 僅僅檢查連續的元素,所以在調用以前須要根據指定的字段將數據排序。

reversed(seq)

reversed函數接收一個序列(實現sequence相關協議,已知長度)

print(list(reversed(range(5))))
## output: [4, 3, 2, 1, 0]

itertools.tee(iterable, n=2)

tee函數返回單個iterable對象的n個獨立迭代器

g1, g2 = itertools.tee('ABC')
print(next(g1), next(g2))
print(next(g1), next(g2))
print(list(zip(*itertools.tee('ABC'))))
"""output
A A
B B
[('A', 'A'), ('B', 'B'), ('C', 'C')]
"""

縮減生成器

接收一個迭代對象,處理只返回一個單一值。

functools.reduce(function, iterable,initializer=None)

function參數是一個接收兩個參數的函數function(x, y),reduce函數將上一次function獲得的返回值做爲參數x,將iterable的下一次迭代值做爲參數y傳進function計算,初始時x的值爲initializer值(若initializer爲None,初始值則爲iterable的第一個元素值)。循環直到iterable耗盡返回最終值。
reduce的基本實現大概爲一下代碼:

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
print(functools.reduce(add, [1, 2, 3, 4, 5]))
## output: 15

經常使用的min和max函數均可以用reduce實現

def min_reduce(iterable):
    return functools.reduce(lambda x, y: x if x < y else y, iterable)

def max_reduce(iterable):
    return functools.reduce(lambda x, y: x if x > y else y, iterable)

print(min_reduce([4, 6, 6, 78]))
print(max_reduce([4, 6, 6, 78]))
"""output
4
78
"""

除此以外any和all函數原理也是相似,再也不闡述。

總結

本篇按照分類介紹了python庫中的一些經常使用的生成器,能夠經過不一樣場景選擇不一樣的生成器工具,將它們組合靈活運用。

相關連接

Python3中的迭代器和生成器

相關文章
相關標籤/搜索