1. 程式人生 > >Python標準庫筆記(10) — itertools模塊

Python標準庫筆記(10) — itertools模塊

構造 values tools multi 生成 TE product and map()

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() 接收和列表切片相同的參數:startstopstepstartstep 參數是可選的。

# 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 參數。

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() 。只要輸入的成員是唯一的,輸出就不會包含任何重復的值。

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')

轉載請註明來源: Python標準庫筆記(10) — itertools模塊

Python標準庫筆記(10) — itertools模塊