Python标准库:itertools迭代器函数
一. itertools迭代器函數
itertools包括一組用于處理序列數據集的函數。這個模塊提供的函數是受函數式編程語言(如Clojure、Haskell、APL和SML)中類似特性的啟發。其目的是要能快速處理,以及要高效地使用內存,而且可以聯結在一起表述更復雜的基于迭代的算法。
與使用列表的代碼相比,基于迭代器的代碼可以提供更好的內存消費特性。在真正需要數據之前,并不從迭代器生成數據,由于這個原因,不需要把所有數據都同時存儲在內存中。這種“懶”處理模式可以減少交換以及大數據集的其他副作用,從而改善性能。
除了itertools中定義的函數,這一節中的例子還會利用一些內置函數完成迭代。
1.合并和分解迭代器
chain()函數取多個迭代器作為參數,最后返回一個迭代器,它會生成所有輸入迭代器的內容,就好像這些內容來自一個迭代器一樣。
from itertools import *for i in chain([1, 2, 3], ['a', 'b', 'c']):print(i, end=' ') print()利用chain(),可以輕松地處理多個序列而不必構造一個很大的列表。
1 2 3 a b c如果不能提前知道所有要結合的迭代器(可迭代對象),或者如果需要采用懶方法計算,那么可以使用chain.from_iterable()來構造這個鏈。
''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:725638078 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' from itertools import *def make_iterables_to_chain():yield [1, 2, 3]yield ['a', 'b', 'c']for i in chain.from_iterable(make_iterables_to_chain()):print(i, end=' ') print()輸出
1 2 3 a b c內置函數zip()返回一個迭代器,它會把多個迭代器的元素結合到一個元組中。
for i in zip([1, 2, 3], ['a', 'b', 'c']):print(i)與這個模塊中的其他函數一樣,返回值是一個可迭代對象,會一次生成一個值。
(1, 'a') (2, 'b') (3, 'c')第一次輸入迭代器處理完時zip()就會停止。要處理所有輸入(即使迭代器生成的值個數不同),則要使用zip_longest()。
from itertools import *r1 = range(3) r2 = range(2) print('zip stops early:') print(list(zip(r1, r2))) r1 = range(3) r2 = range(2) print('\nzip_longest processes all of the values:') print(list(zip_longest(r1, r2)))默認地,zip_longest()會把所有缺少的值替換為None。可以借助fillvalue參數來使用一個不同的替換值。
zip stops early: [(0, 0), (1, 1)]zip_longest processes all of the values: [(0, 0), (1, 1), (2, None)]islice()函數返回一個迭代器,它按索引從輸入迭代器返回所選擇的元素。
''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:725638078 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' from itertools import *print('Stop at 5:') for i in islice(range(100), 5):print(i, end=' ') print('\n') print('Start at 5, Stop at 10:') for i in islice(range(100), 5, 10):print(i, end=' ') print('\n') print('By tens to 100:') for i in islice(range(100), 0, 100, 10):print(i, end=' ') print('\n')islice()與列表的slice操作符參數相同,同樣包括開始位置(start)、結束位置(stop)和步長(step)。start和step參數是可選的。
Stop at 5: 0 1 2 3 4 Start at 5, Stop at 10: 5 6 7 8 9 By tens to 100: 0 10 20 30 40 50 60 70 80 90tee()函數根據一個原輸入迭代器返回多個獨立的迭代器(默認為2個)。
from itertools import *r = islice(count(), 5) i1, i2 = tee(r) print('i1:', list(i1)) print('i2:', list(i2))tee()的語義類似與UNIX tee工具,它會重復從輸入讀到的值,并把它們寫至一個命名文件和標準輸出。tee()返回的迭代器可以用來為并行處理的多個算法提供相同的數據集。
i1: [0, 1, 2, 3, 4] i2: [0, 1, 2, 3, 4]tee()創建的新迭代器會共享其輸入迭代器,所以創建了新迭代器后,不應再使用原迭代器。
from itertools import *r = islice(count(), 5) i1, i2 = tee(r) print('r:', end=' ') for i in r:print(i, end=' ')if i > 1:break print() print('i1:', list(i1)) print('i2:', list(i2))如果原輸入迭代器的一些值已經消費,新迭代器不會再生成這些值。
r: 0 1 2 i1: [3, 4] i2: [3, 4]2.轉換輸入
內置的map()函數返回一個迭代器,它對輸入迭代器中的值調用一個函數并返回結果。任何輸入迭代器中的元素全部消費時,map()函數都會停止。
''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:725638078 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' def times_two(x):return 2 * xdef multiply(x, y):return (x, y, x * y)print('Doubles:') for i in map(times_two, range(5)):print(i) print('\nMultiples:') r1 = range(5) r2 = range(5, 10) for i in map(multiply, r1, r2):print('{:d} * {:d} = {:d}'.format(*i)) print('\nStopping:') r1 = range(5) r2 = range(2) for i in map(multiply, r1, r2):print(i)在第一個例子中,lambda函數將輸入值乘以2。在第二個例子中,lambda函數將兩個參數相乘(這兩個參數分別來自不同的迭代器),返回一個元組,其中包含原參數和計算得到的值。第三個例子會在生成兩個元組后停止,因為第二個區間已經處理完。
Doubles: 0 2 4 6 8Multiples: 0 * 5 = 0 1 * 6 = 6 2 * 7 = 14 3 * 8 = 24 4 * 9 = 36Stopping: (0, 0, 0) (1, 1, 1)starmap()函數類似于map(),不過并不是由多個迭代器構成一個元組,它使用 * 語法分解一個迭代器中的元素作為映射函數的參數。
from itertools import *values = [(0, 5), (1, 6), (2, 7), (3, 8), (4, 9)] for i in starmap(lambda x, y: (x, y, x * y), values):print('{} * {} = {}'.format(*i))map()的映射函數名為f(i1,i2),而傳入starmap()的映射函數名為f(*i)。
0 * 5 = 0 1 * 6 = 6 2 * 7 = 14 3 * 8 = 24 4 * 9 = 363.生成新值
count()函數返回一個迭代器,該迭代器能夠無限地生成連續的整數。第一個數可以作為參數傳入(默認為0)。這里沒有上界參數(參見內置的range(),這個函數對結果集可以有更多控制)。
from itertools import *for i in zip(count(1), ['a', 'b', 'c']):print(i)這個例子會停止,因為列表參數會被完全消費。
(1, 'a') (2, 'b') (3, 'c')count()的“開始位置”和“步長”參數可以是可相加的任意的數字值。
''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:725638078 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' import fractions from itertools import *start = fractions.Fraction(1, 3) step = fractions.Fraction(1, 3) for i in zip(count(start, step), ['a', 'b', 'c']):print('{}: {}'.format(*i))在這個例子中,開始點和步長是來自fraction模塊的Fraction對象。
1/3: a 2/3: b 1: ccycle()函數返回一個迭代器,它會無限地重復給定參數的內容。由于必須記住輸入迭代器的全部內容,所以如果這個迭代器很長,則可能會耗費大量內存。
from itertools import *for i in zip(range(7), cycle(['a', 'b', 'c'])):print(i)這個例子中使用了一個計數器變量,在數個周期后會中止循環。
(0, 'a') (1, 'b') (2, 'c') (3, 'a') (4, 'b') (5, 'c') (6, 'a')repeat()函數返回一個迭代器,每次訪問時會生成相同的值。
from itertools import *for i in repeat('over-and-over', 5):print(i)repeat()返回的迭代器會一直返回數據,除非提供了可選的times參數來限制次數。
over-and-over over-and-over over-and-over over-and-over over-and-over如果即要包含來自其他迭代器的值,也要包含一些不變的值,那么可以結合使用repeat()以及zip()或map()。
''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:725638078 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' from itertools import *for i, s in zip(count(), repeat('over-and-over', 5)):print(i, s)這個例子中就結合了一個計數器值和repeat()返回的常量。
0 over-and-over 1 over-and-over 2 over-and-over 3 over-and-over 4 over-and-over下面這個例子使用map()將0到4區間中的數乘以2。
from itertools import *for i in map(lambda x, y: (x, y, x * y), repeat(2), range(5)):print('{:d} * {:d} = {:d}'.format(*i))repeat()迭代器不需要被顯式限制,因為任何一個輸入迭代器結束時map()就會停止處理,而且range()只返回5個元素.
2 * 0 = 0 2 * 1 = 2 2 * 2 = 4 2 * 3 = 6 2 * 4 = 84.過濾
dropwhile()函數返回一個迭代器,它會在條件第一次變為false之后生成輸入迭代器的元素。
from itertools import *def should_drop(x):print('Testing:', x)return x < 1for i in dropwhile(should_drop, [-1, 0, 1, 2, -2]):print('Yielding:', i)dropwhile()并不會過濾輸入的每一個元素。第一次條件為false之后,輸入迭代器的所有其余元素都會返回。
Testing: -1 Testing: 0 Testing: 1 Yielding: 1 Yielding: 2 Yielding: -2taskwhile()與dropwhile()正相反。它也返回一個迭代器,這個迭代器將返回輸入迭代器中保證測試條件為true的元素。
from itertools import *def should_take(x):print('Testing:', x)return x < 2for i in takewhile(should_take, [-1, 0, 1, 2, -2]):print('Yielding:', i)一旦should_take()返回false,takewhile()就停止處理輸入。
Testing: -1 Yielding: -1 Testing: 0 Yielding: 0 Testing: 1 Yielding: 1 Testing: 2內置函數filter()返回一個迭代器,它只包含測試條件返回true時所對應的元素。
from itertools import *def check_item(x):print('Testing:', x)return x < 1for i in filter(check_item, [-1, 0, 1, 2, -2]):print('Yielding:', i)filter()與dropwhile()和takewhile()不同,它在返回之前會測試每一個元素。
Testing: -1 Yielding: -1 Testing: 0 Yielding: 0 Testing: 1 Testing: 2 Testing: -2 Yielding: -2filterfalse()返回一個迭代器,其中只包含測試條件返回false時對應的元素。
from itertools import *def check_item(x):print('Testing:', x)return x < 1for i in filterfalse(check_item, [-1, 0, 1, 2, -2]):print('Yielding:', i)check_item()中的測試表達式與前面相同,所以在這個使用filterfalse()的例子中,結果與上一個例子的結果正好相反。
Testing: -1 Testing: 0 Testing: 1 Yielding: 1 Testing: 2 Yielding: 2 Testing: -2compress()提供了另一種過濾可迭代對象內容的方法。不是調用一個函數,而是使用另一個可迭代對象中的值指示什么時候接受一個值以及什么時候互虐一個值。
from itertools import *every_third = cycle([False, False, True]) data = range(1, 10) for i in compress(data, every_third):print(i, end=' ') print()第一個參數是要處理的數據迭代器。第二個參數是一個選擇器迭代器,這個迭代器會生成布爾值指示從數據輸入中取哪些元素(true值說明將生成這個值;false值表示這個值將被忽略)。
3 6 95.數據分組
groupby()函數返回一個迭代器,它會生成按一個公共鍵組織的值集。下面這個例子展示了如何根據一個屬性對相關的值分組。
''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:725638078 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' import functools from itertools import * import operator import pprint@functools.total_ordering class Point:def __init__(self, x, y):self.x = xself.y = ydef __repr__(self):return '({}, {})'.format(self.x, self.y)def __eq__(self, other):return (self.x, self.y) == (other.x, other.y)def __gt__(self, other):return (self.x, self.y) > (other.x, other.y)# Create a dataset of Point instances data = list(map(Point,cycle(islice(count(), 3)),islice(count(), 7))) print('Data:') pprint.pprint(data, width=35) print() # Try to group the unsorted data based on X values print('Grouped, unsorted:') for k, g in groupby(data, operator.attrgetter('x')):print(k, list(g)) print() # Sort the data data.sort() print('Sorted:') pprint.pprint(data, width=35) print() # Group the sorted data based on X values print('Grouped, sorted:') for k, g in groupby(data, operator.attrgetter('x')):print(k, list(g)) print()輸入序列要根據鍵值排序,以保證得到預期的分組。
Data: [(0, 0),(1, 1),(2, 2),(0, 3),(1, 4),(2, 5),(0, 6)]Grouped, unsorted: 0 [(0, 0)] 1 [(1, 1)] 2 [(2, 2)] 0 [(0, 3)] 1 [(1, 4)] 2 [(2, 5)] 0 [(0, 6)]Sorted: [(0, 0),(0, 3),(0, 6),(1, 1),(1, 4),(2, 2),(2, 5)]Grouped, sorted: 0 [(0, 0), (0, 3), (0, 6)] 1 [(1, 1), (1, 4)] 2 [(2, 2), (2, 5)]6.合并輸入
accumulate()函數處理輸入迭代器,向一個函數傳遞第n和n+1個元素,并且生成返回值而不是某個輸入。合并兩個值的默認函數會將兩個值相加,所以accumulate()可以用來生成一個數值輸入序列的累加和。
from itertools import *print(list(accumulate(range(5)))) print(list(accumulate('abcde')))用于非整數值序列時,結果取決于將兩個元素“相加”是什么含義。這個腳本中的第二個例子顯示了當accumulate()接收到一個字符串輸入時,每個相應都將是該字符串的一個前綴,而且長度不斷增加。
[0, 1, 3, 6, 10] ['a', 'ab', 'abc', 'abcd', 'abcde']accumulate()可以與任何取兩個輸入值的函數結合來得到不同的結果。
from itertools import *def f(a, b):print(a, b)return b + a + bprint(list(accumulate('abcde', f)))這個例子以一種特殊的方式合并字符串值,會生成一系列(無意義的)回文。每一步調用f()時,它都會打印accumulate()傳入的輸入值。
a b bab c cbabc d dcbabcd e ['a', 'bab', 'cbabc', 'dcbabcd', 'edcbabcde']迭代處理多個序列的嵌套for循環通常可以被替換為product(),它會生成一個迭代器,值為輸入值集合的笛卡爾積。
from itertools import * import pprintFACE_CARDS = ('J', 'Q', 'K', 'A') SUITS = ('H', 'D', 'C', 'S') DECK = list(product(chain(range(2, 11), FACE_CARDS),SUITS,) ) for card in DECK:print('{:>2}{}'.format(*card), end=' ')if card[1] == SUITS[-1]:print()product()生成的值是元組,成員取自作為參數傳入的各個迭代器(按其傳入的順序)。返回的第一個元組包含各個迭代器的第一個值。傳入product()的最后一個迭代器最先處理,接下來處理倒數第二個迭代器,依此類推。結果是按第一個迭代器、下一個迭代器等的順序得到的返回值。
在這個例子中,撲克牌首先按牌面大小排序,然后按花色排序。
2H 2D 2C 2S 3H 3D 3C 3S 4H 4D 4C 4S 5H 5D 5C 5S 6H 6D 6C 6S 7H 7D 7C 7S 8H 8D 8C 8S 9H 9D 9C 9S 10H 10D 10C 10S JH JD JC JS QH QD QC QS KH KD KC KS AH AD AC AS要改變這些撲克牌的順序,需要改變傳入product()的參數的順序。
from itertools import *FACE_CARDS = ('J', 'Q', 'K', 'A') SUITS = ('H', 'D', 'C', 'S') DECK = list(product(SUITS,chain(range(2, 11), FACE_CARDS),) ) for card in DECK:print('{:>2}{}'.format(card[1], card[0]), end=' ')if card[1] == FACE_CARDS[-1]:print()這個例子中的打印循環會查找一個A而不是黑桃,然后增加一個換行使輸出分行顯示。
2H 3H 4H 5H 6H 7H 8H 9H 10H JH QH KH AH 2D 3D 4D 5D 6D 7D 8D 9D 10D JD QD KD AD 2C 3C 4C 5C 6C 7C 8C 9C 10C JC QC KC AC 2S 3S 4S 5S 6S 7S 8S 9S 10S JS QS KS AS要計算一個序列與自身的積,開源指定輸入重復多少次。
''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:725638078 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' from itertools import *def show(iterable):for i, item in enumerate(iterable, 1):print(item, end=' ')if (i % 3) == 0:print()print()print('Repeat 2:\n') show(list(product(range(3), repeat=2))) print('Repeat 3:\n') show(list(product(range(3), repeat=3)))由于重復一個迭代器就像把同一個迭代器傳入多次,product()生成的每個元組所包含的元素個數就等于重復計數器。
Repeat 2:(0, 0) (0, 1) (0, 2) (1, 0) (1, 1) (1, 2) (2, 0) (2, 1) (2, 2) Repeat 3:(0, 0, 0) (0, 0, 1) (0, 0, 2) (0, 1, 0) (0, 1, 1) (0, 1, 2) (0, 2, 0) (0, 2, 1) (0, 2, 2) (1, 0, 0) (1, 0, 1) (1, 0, 2) (1, 1, 0) (1, 1, 1) (1, 1, 2) (1, 2, 0) (1, 2, 1) (1, 2, 2) (2, 0, 0) (2, 0, 1) (2, 0, 2) (2, 1, 0) (2, 1, 1) (2, 1, 2) (2, 2, 0) (2, 2, 1) (2, 2, 2)permutations()函數從輸入迭代器生成元素,這些元素以給定長度的排列形式組合。默認地它會生成所有排列的全集。
from itertools import *def show(iterable):first = Nonefor i, item in enumerate(iterable, 1):if first != item[0]:if first is not None:print()first = item[0]print(''.join(item), end=' ')print()print('All permutations:\n') show(permutations('abcd')) print('\nPairs:\n') show(permutations('abcd', r=2))可以使用r參數限制返回的各個排列的長度和個數。
All permutations:abcd abdc acbd acdb adbc adcb bacd badc bcad bcda bdac bdca cabd cadb cbad cbda cdab cdba dabc dacb dbac dbca dcab dcba Pairs:ab ac ad ba bc bd ca cb cd da db dc為了將值限制為唯一的組合而不是排列,可以使用combinations()。只要輸入的成員是唯一的,輸出就不會包含任何重復的值。
from itertools import *def show(iterable):first = Nonefor i, item in enumerate(iterable, 1):if first != item[0]:if first is not None:print()first = item[0]print(''.join(item), end=' ')print()print('Unique pairs:\n')show(combinations('abcd', r=2))與排列不同,combinations()的r參數是必要參數。
Unique pairs:ab ac ad bc bd cd盡管combinations()不會重復單個的輸入元素,但有時可能也需要考慮包含重復的元素組合。對于這種情況,可以使用combinations_with_replacement()。
''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:725638078 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' from itertools import *def show(iterable):first = Nonefor i, item in enumerate(iterable, 1):if first != item[0]:if first is not None:print()first = item[0]print(''.join(item), end=' ')print()print('Unique pairs:\n') show(combinations_with_replacement('abcd', r=2))在這個輸出中,每個輸入元素會與自身以及輸入序列的所有其他成員配對。
Unique pairs:aa ab ac ad bb bc bd cc cd dd總結
以上是生活随笔為你收集整理的Python标准库:itertools迭代器函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 介绍一个非常好用的Python模块-pp
- 下一篇: 两种思路将Python中两个有序数组合并