程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Python教程:itertools模塊

編輯:Python

itertools 用於更高效地創建迭代器的函數工具。

itertools 提供的功能受Clojure,Haskell,APL和SML等函數式編程語言的類似功能的啟發。它們的目的是快速有效地使用內存,並且將它們關聯在一起以表示更復雜的基於迭代的算法。

基於迭代器的代碼比使用列表的代碼提供了更好的內存消耗特性。因為直到數據需要使用時才從迭代器中生成,所有數據不需要同時存儲在內存中。這種 “惰性” 的處理模式可以減少大型數據集的交換和其他副作用,從而提高性能。

除了 itertools 中定義的函數之外,本文中的示例還使用了一些內置函數進行迭代。

1.合並和分割

chain() 函數將多個迭代器作為參數,並返回一個迭代器,這樣它生成所有輸入的內容,就像來自於單個迭代器一樣。

from itertools import chain
for i in chain([1, 2, 3], ['a', 'b', 'c']):
print(i, end=' ')

使用 chain() 可以輕松地處理多個序列,而不需要生成一個更大的序列。

# OutPut
1 2 3 a b c

如果要組合的迭代不是全部預先顯式聲明的,或者需要惰性計算的,則可以使用 chain.from_iterable() 來替換 chain() 。

from itertools import chain
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=' ')
# OutPut
1 2 3 a b c

Python內置函數 zip() 也是返回一個迭代器,但它是將幾個迭代器的元素組合成元組。

for i in zip([1, 2, 3], ['a', 'b', 'c']):
print(i)

zip() 和本模塊中的其他函數一樣,返回一個可迭代的對象,每次迭代產生一個值。

# OutPut
(1, 'a')
(2, 'b')
(3, 'c')

但是, 使用 zip() 時當第一個輸入迭代器耗盡時,zip() 就會停止。如果要處理所有的輸入,即使迭代器產生不同數量的值,那麼可以使用 zip_longest() 。

from itertools import zip_longest
r1 = range(3)
r2 = range(2)
print('使用zip會提前結果迭代:')
print(list(zip(r1, r2)))
print()
print('zip_longest會處理完所有值:')
print(list(zip_longest(r1, r2)))

默認情況下,zip_longest() 會使用 None 來填充缺失位置的值。使用 fillvalue 參數來設置不同的替代值。

# OutPut
使用zip會提前結果迭代:
[(0, 0), (1, 1)]
zip_longest會處理完所有值:
[(0, 0), (1, 1), (2, None)]

islice() 函數返回一個迭代器,用於通過索引返回輸入迭代器的指定項。

from itertools import islice
print('Stop at 5:')
for i in islice(range(100), 5):
print(i, end=' ')
print()
print('Start at 5, Stop at 10:')
for i in islice(range(100), 5, 10):
print(i, end=' ')
print()
print('By tens to 100:')
for i in islice(range(100), 0, 100, 10):
print(i, end=' ')
print()

islice() 接收和列表切片相同的參數:start , stop 和 step 。 start 和 step 參數是可選的。

# OutPut
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 90

tee() 函數作用是根據單個原始輸入返回多個獨立的迭代器(默認為兩個)。

from itertools import islice, tee
r = islice(range(10), 5)
i1, i2 = tee(r)
print('i1:', list(i1))
print('i2:', list(i2))

tee() 具有與Unix tee 實用程序類似的語義,它從它的輸入中重復地讀取的值並將它們寫入一個命名文件和標准輸出。 通過 tee() 函數可以將同一組數據提供給多個算法並行處理。

# OutPut
i1: [0, 1, 2, 3, 4]
i2: [0, 1, 2, 3, 4]

需要注意,由 tee() 創建的新迭代器將共享它們的輸入,因此在創建新迭代器後不要再使用輸入的迭代器。

from itertools import islice, tee
r = islice(range(10), 5)
i1, i2 = tee(r)
print('迭代原始:', end=' ')
for i in r:
print(i, end=' ')
if i > 1:
break
print()
print('i1:', list(i1))
print('i2:', list(i2))

如果原始迭代器已經消耗了一些值,那麼新的迭代器將不會生成這些值。

# OutPut
迭代原始: 0 1 2
i1: [3, 4]
i2: [3, 4]

2.計算輸入

Python內置的 map() 函數返回一個迭代器。 該迭代器根據輸入迭代器中的值調用函數,並返回結果。當任意一個輸入迭代器耗盡時它就立刻停止。

def times_two(x):
return 2 * x
def multiply(x, y):
return (x, y, x * y)
print('單個輸入:')
for i in map(times_two, range(5)):
print(i, end=' ')
print('\n多個輸入:')
r1 = range(5)
r2 = range(5, 10)
for i in map(multiply, r1, r2):
print('{:d} * {:d} = {:d}'.format(*i))
print('\n迭代停止:')
r1 = range(5)
r2 = range(2)
for i in map(multiply, r1, r2):
print(i)

在第一個例子中,函數將所有輸入值乘以2。在第二個例子中,函數將從兩個單獨的迭代器中獲取的兩個參數相乘,並返回一個包含原始參數和計算值的元組。第三個例子中,在生成了兩個元組之後便停止了,因為第二個輸入已經耗盡。

# OutPut
單個輸入:
0 2 4 6 8
多個輸入:
0 * 5 = 0
1 * 6 = 6
2 * 7 = 14
3 * 8 = 24
4 * 9 = 36
迭代停止:
(0, 0, 0)
(1, 1, 1)

starmap() 函數與 map() 類似,但不是從多個迭代器構造元組,而是使用 * 語法將單個迭代器中的項作為參數解包給map函數。

from itertools import starmap
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) 。

#OutPut
0 * 5 = 0
1 * 6 = 6
2 * 7 = 14
3 * 8 = 24
4 * 9 = 36

3.產生新值

count() 函數會返回一個可以無限地產生連續整數的迭代器。第一個數字可以作為參數傳遞(默認值為0)。沒有上限參數(有關對結果集的更多控制,請參閱內置的 range())。

from itertools import count
for i in zip(count(1), ['a', 'b', 'c']):
print(i)

此示例因為使用了 zip() 和有限長度列表參數所以才停止。

# OutPut
(1, 'a')
(2, 'b')
(3, 'c')

count() 的start和step參數可以是任何可以加在一起的數字值。

import fractions
from itertools import count
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 對象。

# OutPut
1/3: a
2/3: b
1: c

cycle() 函數的作用是:返回一個迭代器,該迭代器重復無限地給出的參數的內容。因為它必須記住輸入迭代器的全部內容,所以如果迭代器很長,它可能會消耗相當多的內存。

from itertools import cycle
for i in cycle(['a', 'b', 'c']):
print(i)

如果沒有打斷,它會無限循環下去。

# OutPut
a
b
c
a
b
...

repeat() 函數的作用是:返回一個迭代器,該迭代器每次訪問時都會產生相同的值。

from itertools import repeat
for i in repeat('over-and-over', times=5):
print(i)

repeat() 返回的迭代器將不斷返回數據,除非提供可選的times參數來限制次數。

# OutPut
over-and-over
over-and-over
over-and-over
over-and-over
over-and-over

當需要將某個固定值包含在其他迭代器的值中時,使用 repeat() 與 zip() 或 map() 組合會很有用。

from itertools import repeat, count
for i, s in zip(count(), repeat('over-and-over', 5)):
print(i, s)

在本例中,count值與 repeat() 返回的常量組合在一起。

此示例使用 map() 將從0到4的數字乘以2。

from itertools import repeat
for i in map(lambda x, y: (x, y, x * y), repeat(2), range(5)):
print('{:d} * {:d} = {:d}'.format(*i))

本例中 repeat() 不需要顯式限制迭代次數,因為 range() 只返回五個元素, map() 在其任意輸入結束時會停止處理。

# OutPut
2 * 0 = 0
2 * 1 = 2
2 * 2 = 4
2 * 3 = 6
2 * 4 = 8

4.過濾

dropwhile() 函數的作用是:返回一個迭代器,直到條件第一次為false時,該迭代器開始才產生輸入迭代器的元素。

from itertools import dropwhile
def should_drop(x):
print('輸入:', x)
return x < 1
for i in dropwhile(should_drop, [-1, 0, 1, 2, -2]):
print('產出:', i)

dropwhile() 不會過濾每個輸入項; 當第一次條件為假後,便直接返回輸入中的所有剩余項目。

# OutPut
輸入: -1
輸入: 0
輸入: 1
產出: 1
產出: 2
產出: -2

與 dropwhile() 相反的是 takewhile() 。它返回一個迭代器,只要測試函數返回true, 該迭代器就返回輸入迭代器中的項目。

from itertools import takewhile
def should_take(x):
print('輸入:', x)
return x < 1
for i in takewhile(should_take, [-1, 0, 1, 2, -2]):
print('產生:', i)

一旦should_take()返回 False, takewhile()就停止處理輸入。

# OutPut
輸入: -1
產生: -1
輸入: 0
產生: 0
輸入: 1

Python內置函數 filter() 是返回一個包含測試函數返回true的所有項的迭代器。

def check_item(x):
print('輸入:', x)
return x < 1
for i in filter(check_item, [-1, 0, 1, 2, -2]):
print('產出:', i)

filter() 不同於 dropwhile() 和 takewhile() 的是,filter() 每個項目在返回之前都代入測試函數。

# OutPut
輸入: -1
產出: -1
輸入: 0
產出: 0
輸入: 1
輸入: 2
輸入: -2
產出: -2

filterfalse() 返回一個迭代器,該迭代器只包含測試函數返回false的項。

from itertools import filterfalse
def check_item(x):
print('輸入:', x)
return x < 1
for i in filterfalse(check_item, [-1, 0, 1, 2, -2]):
print('產出:', i)

測試函數 check_item() 和上例中的一樣,但是返回的結果正好和 filter() 相反。

# OutPut
輸入: -1
輸入: 0
輸入: 1
產出: 1
輸入: 2
產出: 2
輸入: -2

compress() 提供了另一種過濾可迭代內容的方法。它不再是調用函數,而是使用另一個迭代中的值來指示何時接受值何時忽略值。

from itertools import compress, cycle
every_third = cycle([False, False, True])
data = range(1, 10)
for i in compress(data, every_third):
print(i, end=' ')

compress() 的第一個參數是需要進行處理的可迭代數據,第二個參數是可迭代的生成的布爾值選擇器,指示從數據輸入中取出哪些元素(True產生值,False忽略)。

# OutPut
3 6 9

5.聚合

groupby() 函數返回一個迭代器,該迭代器生成由公共鍵聚合的值集。下面例子展示基於屬性對相關值進行分組。

from itertools import groupby
import functools
import operator
import pprint
@functools.total_ordering
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __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)
# 生成Point實例的數據集
data = list(map(Point, [1, 2, 3, 1, 2], range(5)))
print('Data:')
pprint.pprint(data, width=35)
print()
# 對無序的data基於屬性x聚合
print('聚合, 無序data:')
for k, g in groupby(data, operator.attrgetter('x')):
print(k, list(g))
print()
# 對data排序
data.sort()
print('排序後:')
pprint.pprint(data, width=35)
print()
# 對排序後的data基於屬性X聚合
print('聚合, 有序data:')
for k, g in groupby(data, operator.attrgetter('x')):
print(k, list(g))

輸入序列需要根據鍵值進行排序處理後才輸出預期的聚合結果。

# OutPut
Data:
[(1, 0),
(2, 1),
(3, 2),
(1, 3),
(2, 4)]
聚合, 無序data:
1 [(1, 0)]
2 [(2, 1)]
3 [(3, 2)]
1 [(1, 3)]
2 [(2, 4)]
排序後:
[(1, 0),
(1, 3),
(2, 1),
(2, 4),
(3, 2)]
聚合, 有序data:
1 [(1, 0), (1, 3)]
2 [(2, 1), (2, 4)]
3 [(3, 2)]

6.組合

accumulate() 函數的作用是:處理可迭代的輸入,將第n和n+1項傳遞給目標函數,生成返回值,而不是直接返回輸入。默認函數功能是將兩個值相加,因此可以使用 accumulate() 來生成一系列數值輸入的累積和。

from itertools import accumulate
print(list(accumulate(range(5))))
print(list(accumulate('abcde')))

如果輸入序列是非整數值時,結果取決於將兩個項“相加”在一起的含義。比如上面例子中的第二項 accumulate() 接收的是一個字符串,返回則是將字符串逐個拼接在一起。

# OutPut
[0, 1, 3, 6, 10]
['a', 'ab', 'abc', 'abcd', 'abcde']

同時 accumulate() 也接受自定義的帶有兩個輸入項的函數。

from itertools import accumulate
def f(a, b):
print(a, b)
return b + a
print(list(accumulate('abcde', f)))
# OutPut
a b
ba c
cba d
dcba e
['a', 'ba', 'cba', 'dcba', 'edcba']

如果嵌套for循環遍歷多個序列可以使用 product() ,它會生成一個迭代器,其值是該組輸入值的笛卡爾乘積。

from itertools import product
char = ['a', 'b', 'c']
integer = [1, 2, 3]
for each in product(char, integer):
print(each)

由 product() 產生的值是元組,由每個迭代中取出的成員按照它們傳遞的順序作為參數傳入。

# OutPut
('a', 1)
('a', 2)
('a', 3)
('b', 1)
('b', 2)
('b', 3)
('c', 1)
('c', 2)
('c', 3)

如果要計算序列與其本身的笛卡爾積,則需要指定 repeat 參數。

''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:711312441 尋找有志同道合的小伙伴,互幫互助,群裡還有不錯的視頻學習教程和PDF電子書! '''
from itertools import product
char = ['a', 'b']
for each in product(char, repeat=2):
print(each)
for each in product(char, repeat=2):
print(each)
# OutPut
('a', 'a', 'a')
('a', 'a', 'b')
('a', 'b', 'a')
('a', 'b', 'b')
('b', 'a', 'a')
('b', 'a', 'b')
('b', 'b', 'a')
('b', 'b', 'b')

permutation() 函數從輸入的迭代的組合中生成指定長度的排列。它默認生成所有排列的完整集合。

from itertools import permutations
for each in permutations('abc'):
print(each)
print()
for each in permutations('abc', r=2):
print(each)

使用 r 參數來限制返回的單個排列的長度。

# OutPut
('a', 'b', 'c')
('a', 'c', 'b')
('b', 'a', 'c')
('b', 'c', 'a')
('c', 'a', 'b')
('c', 'b', 'a')
('a', 'b')
('a', 'c')
('b', 'a')
('b', 'c')
('c', 'a')
('c', 'b')

如果輸出要保證唯一, 即需要組合而不是排列,請使用 combination() 。只要輸入的成員是唯一的,輸出就不會包含任何重復的值。

''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:711312441 尋找有志同道合的小伙伴,互幫互助,群裡還有不錯的視頻學習教程和PDF電子書! '''
from itertools import combinations
for each in combinations('abc', r=2):
print(each)

與 permutations() 不同的是, combination() 必須傳入 r 參數。

# OutPut
('a', 'b')
('a', 'c')
('b', 'c')

因為 combination() 不重復單個輸入元素,但考慮有時需要包含重復元素的組合。對於這些情況,可以使用 combinations_with_replacement() 。

from itertools import combinations_with_replacement
for each in combinations_with_replacement('abc', r=2):
print(each)

在此輸出中,每個輸入項都與其自身以及輸入序列的所有其他成員組合。

('a', 'a')
('a', 'b')
('a', 'c')
('b', 'b')
('b', 'c')
('c', 'c')

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved