中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

在Python中按需處理數(shù)據(jù),第2部分: 神奇的 itertools

2018-08-07    來(lái)源:raincent

容器云強(qiáng)勢(shì)上線!快速搭建集群,上萬(wàn)Linux鏡像隨意使用
在此系列的第 1 部分中, 您了解了 Python 迭代器,并學(xué)會(huì)了創(chuàng)建迭代器的最簡(jiǎn)單的方法,即通過(guò)編寫(xiě)生成器函數(shù)或表達(dá)式來(lái)創(chuàng)建迭代器。一旦掌握了基礎(chǔ)知識(shí),就可以很快熟悉構(gòu)建迭代器的各種方法。但是,我們希望最好避免重復(fù)工作。

Python 標(biāo)準(zhǔn)庫(kù)包含了一些非常有用的支持和工具,堪稱(chēng)奇跡,其中有些工具比其他工具更隱蔽一些。有幾個(gè)模塊在操作迭代器時(shí)很有用,但首先您應(yīng)查看內(nèi)置功能中的一些工具,“內(nèi)置”意味著,在大多數(shù)情況下,您無(wú)需導(dǎo)入任何內(nèi)容即可進(jìn)行使用。
 

使用 map 構(gòu)建迭代器


使用 map 函數(shù),您可以處理迭代器中的每一個(gè)項(xiàng),以便生成新的迭代器。

>>> r = range(10)
>>> m = map(str, r)
>>> next(m)
'0'
>>> list(m)
['1', '2', '3', '4', '5', '6', '7', '8', '9']

map 的第一個(gè)自變量是映射函數(shù),即應(yīng)用于每個(gè)項(xiàng)的函數(shù)。 在此例中,我使用 str 將范圍中的每個(gè)整數(shù)轉(zhuǎn)換為字符串。當(dāng)我通過(guò) map 迭代器請(qǐng)求第一項(xiàng)時(shí),我會(huì)收到一個(gè)字符串。然后,我使用 list 來(lái)抽取序列中的所有內(nèi)容。請(qǐng)注意,列表是從 1(而不是 0)開(kāi)始。這是因?yàn)槲乙淹ㄟ^(guò) map 抽取了第一項(xiàng)。與列表或元組不同,迭代器不支持隨機(jī)訪問(wèn)。您可以正向依次獲取各個(gè)項(xiàng)。

當(dāng)然,您可以編寫(xiě)自己的映射函數(shù)。它期望收到來(lái)自 map 的一個(gè)輸入值。將以下內(nèi)容粘貼到解釋器會(huì)話中:

def get_letter(i): 
return chr(ord('a')+i)

此處,我使用 ord 獲取與字符 'a' 對(duì)應(yīng)的數(shù)字代碼,并添加函數(shù)的自變量以派生新的字符代碼。函數(shù) chr 將此代碼重新轉(zhuǎn)換為字符串字符。將此函數(shù)與 map 結(jié)合使用。

>>> r = range(10)
>>> list(map(get_letter, r))
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']

Map 與生成器表達(dá)式


此時(shí),您可能會(huì)記起第 1 部分,并意識(shí)到這很像生成器表達(dá)式。的確,我可以做以下調(diào)整。

>>> list( ( get_letter(i) for i in range(10) ) )
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']

請(qǐng)注意我是如何將生成器表達(dá)式作為自變量直接放入 list。 這說(shuō)明了一點(diǎn),您可以像使用其他 Python 表達(dá)式一樣使用生成器表達(dá)式。在此例中,get_letter 函數(shù)非常簡(jiǎn)單,您甚至可以將它直接構(gòu)建到生成器表達(dá)式中。

>>> list( ( chr(ord('a')+i) for i in range(10) ) )
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']

我似乎有點(diǎn)啰嗦了,但是在 Python 中,map 的存在時(shí)間要比生成器表達(dá)式長(zhǎng),并且在某些特殊情況下它使用起來(lái)很方便。然而,在大多數(shù)情況下,您可能會(huì)使用生成器表達(dá)式來(lái)簡(jiǎn)單操作迭代器。
 

通過(guò)迭代器實(shí)現(xiàn)降維


有時(shí),您并不想將一個(gè)迭代器轉(zhuǎn)換為另一個(gè)迭代器,而只是想通過(guò)迭代器來(lái)計(jì)算一個(gè)值。

>>> sum(range(20, 30))
245

sum 函數(shù)接受數(shù)字迭代器(整數(shù)或浮點(diǎn)數(shù))并返回迭代器中所有項(xiàng)的總和,在此例中是 20 到 30 之間所有數(shù)字的總和。這稱(chēng)為“降維”,即,將一系列值降維為一個(gè)新值。Python 還提供了一個(gè)用于降維的通用函數(shù)。首先粘貼以下函數(shù):

def sum_of_squares(acc, i): 
return acc + i**2

使用此函數(shù)實(shí)現(xiàn)降維,如下所示。您需要 functools 模塊。

>>> import functools
>>> functools.reduce(sum_of_squares, range(10))
285

第一個(gè)自變量是接受兩個(gè)自變量的函數(shù)。它接受一個(gè)累積值,隨后接受一個(gè)新值以某種方式組合成一個(gè)更新的累積值,然后返回此值。在此例中,累積值是到目前為止各項(xiàng)的平方和。

就像生成器表達(dá)式的可讀性比 map 的可讀性更強(qiáng)一些一樣,通常有一些可讀性更強(qiáng)的替代方法可用于實(shí)現(xiàn)降維,但此函數(shù)對(duì)于某些特定用途很有用(它在概念上是 map 的對(duì)應(yīng)項(xiàng))。

map 接受一個(gè)迭代器并生成一個(gè)派生迭代器。reduce 接受一個(gè)迭代器并返回由所有項(xiàng)派生的單個(gè)值。這些概念共同構(gòu)成一種處理大量數(shù)據(jù)的強(qiáng)大方法。
 

string.join 方法


我想介紹一個(gè)用于降維的特殊方法。

>>> tenletters = ( chr(ord('a')+i) for i in range(10) )
>>> '!'.join(tenletters)
'a!b!c!d!e!f!g!h!i!j'

針對(duì)字符串的 join 方法接受一個(gè)迭代器,并通過(guò)用指定的間隔字符串將迭代器項(xiàng)字符串連接在一起來(lái)創(chuàng)建新字符串。更復(fù)雜一點(diǎn):

>>> '!'.join(tenletters)
''
>>> tenletters = ( chr(ord('a')+i) for i in range(10) )
>>> ''.join(tenletters)
'abcdefghij'
>>> tenletters = ( chr(ord('a')+i) for i in range(10) )
>>> ' and '.join(tenletters)
'a and b and c and d and e and f and g and h and i and j'

第一行的輸出明確表明了迭代器是單向的,一旦用完,就不會(huì)再收到任何輸出。請(qǐng)注意,在使用迭代器的代碼中,這是引起錯(cuò)誤的常見(jiàn)原因。在下一行中,我設(shè)置了一個(gè)新的生成器,您可以看到針對(duì)空字符串的連接只是將給定迭代器中的所有字符串連接在一起。您還可以連接更長(zhǎng)的字符串,如最后幾行所示。
 

過(guò)濾


這里還有一個(gè)內(nèi)置函數(shù),可用于創(chuàng)建僅包含一部分輸入的迭代器。在 第 1 部分中,我提供了以下示例。

>>> import math
>>> notby2or3 = ( n for n in range(1, 20) if math.gcd(n, 2) == 1 and math.gcd(n, 3) == 1 )
>>> list(notby2or3)
[1, 5, 7, 11, 13, 17, 19]

如果您需要回顧 notby2or3 函數(shù),請(qǐng)返回并查看第 1 部分。您可以使用 filter 函數(shù)實(shí)現(xiàn)這個(gè)生成器表達(dá)式的同等功能,重申一遍,filter 函數(shù)非常值得我們關(guān)注。

>>> import math
>>> def notby2or3(n): 
...return math.gcd(n, 2) == 1 and math.gcd(n, 3) == 1
... 
>>> list(filter(notby2or3, range(1, 20)))
[1, 5, 7, 11, 13, 17, 19]

我可以使用所謂的 lambda 函數(shù)來(lái)用一兩行來(lái)編寫(xiě)此功能,但這超出了本教程系列的范圍。
 

itertools 簡(jiǎn)介


使用迭代器(前面反復(fù)提到)時(shí),可以使用許多小模式。itertools 模塊提供了一種高性能方法可實(shí)現(xiàn)其中許多模式,非常值得學(xué)習(xí)。

假設(shè)您想要?jiǎng)?chuàng)建一個(gè)更長(zhǎng)的迭代器來(lái)用于連接一系列其他迭代器:

>>> import itertools
>>> it = itertools.chain(range(5), range(5, 0, -1), range(5))
>>> list(it)
[0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4]

將迭代器作為函數(shù)自變量傳遞給 itertools.chain,這將生成這些迭代器的輸出鏈。如果您具有迭代器的列表或元組序列,那么可以使用 Python 的特殊語(yǔ)法來(lái)表示變量位置函數(shù)自變量。

>>> list_of_iters = [range(5), range(5, 0, -1), range(5)]
>>> it = itertools.chain(*list_of_iters)
>>> list(it)
[0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4]

您可以將任何迭代器用于函數(shù)變量自變量。不必是列表或元組。粘貼以下內(nèi)容:

def forth_back_forth(n): 
yield range(n)
yield range(n, 0, -1)
yield range(n)

您可以使用此生成器為 itertools.chain 提供自變量:

>>> it = itertools.chain(*forth_back_forth(3))
>>> list(it)
[0, 1, 2, 3, 2, 1, 0, 1, 2]

無(wú)限迭代器


我想知道,組合使用 forth_back_forthitertools.chain,并進(jìn)一步執(zhí)行后退/前進(jìn)/后退數(shù)字模式會(huì)產(chǎn)生怎樣的結(jié)果。粘貼以下內(nèi)容:

def zigzag_iters(period): 
while True: 
yield range(period)
yield range(period, 0, -1)

然后,嘗試一下:

>>> it = zigzag_iters(2)
>>> next(it)
range(0, 2)
>>> next(it)
range(2, 0, -1)
>>> next(it)
range(0, 2)
>>> next(it)
range(2, 0, -1)
>>> next(it)
range(0, 2)

如果您繼續(xù)輸入 next(it),將繼續(xù)在兩個(gè)輸出范圍之間反復(fù)循環(huán),因?yàn)?while True 會(huì)實(shí)現(xiàn)無(wú)限循環(huán),其內(nèi)部沒(méi)有代碼可用于中斷循環(huán)的執(zhí)行。此生成器持續(xù)處于暫掛和恢復(fù)狀態(tài),直到 Python 進(jìn)程(在此例中為交互式解釋器)終止;蛘,您可以對(duì)任何生成器對(duì)象使用 close() 方法來(lái)結(jié)束它,而無(wú)論它在何處執(zhí)行。

您可能已經(jīng)注意到,我沒(méi)有使用 list 來(lái)說(shuō)明迭代器的內(nèi)容。如果您嘗試這樣做,基本上是在嘗試創(chuàng)建一個(gè)無(wú)限列表,當(dāng)然,您將耗盡內(nèi)存。

另外請(qǐng)記住,這將返回迭代器,而不是項(xiàng)。對(duì)于 forth_back_forth,建議使用 itertools.chain 從這些迭代器中獲取項(xiàng)。您不能使用 zigzag_iters 來(lái)完成這項(xiàng)任務(wù),因?yàn)樗矔?huì)嘗試創(chuàng)建一個(gè)無(wú)限項(xiàng)的集合,并且在執(zhí)行函數(shù)之前,您必須知道函數(shù)的所有自變量,即使在變量自變量的情況下也是如此。這意味著 Python 運(yùn)行時(shí)將嘗試從無(wú)限迭代器中獲取所有項(xiàng),這是不可能的。

在開(kāi)始使用生成器后,無(wú)限迭代器實(shí)際上是一個(gè)有用的概念。但是,您必須知道要嘗試通過(guò)此類(lèi)迭代器的內(nèi)容構(gòu)建任何集合的操作。
 

級(jí)聯(lián)迭代器


因?yàn)椴荒軐?itertools.chainzigzag_iters 一起使用,因此請(qǐng)創(chuàng)建一個(gè)更直接的版本。

def zigzag(period): 
while True: 
for n in range(period): 
yield n
for n in range(period, 0, -1): 
yield n

粘貼以下內(nèi)容并查看迭代器如何無(wú)限地繼續(xù)下去:

>>> it = zigzag(2)
>>> next(it)
0
>>> next(it)
1
>>> next(it)
2
>>> next(it)
1
>>> next(it)
0
>>> next(it)
1
>>> next(it)
2
>>> next(it)
1
>>> next(it)
2
>>> next(it)
1
>>> next(it)
0
>>> next(it)
1

這是另一種常見(jiàn)模式,其中外部迭代器是一個(gè)或多個(gè)內(nèi)部迭代器的級(jí)聯(lián)形式。Python 為它提供了標(biāo)準(zhǔn)語(yǔ)法,即 yield from 語(yǔ)句,您可以在下面重寫(xiě)的功能相同的 zigzag 中看到此語(yǔ)句。

def zigzag(period): 
while True: 
yield from range(period)
yield from range(period, 0, -1)

如果您想要一個(gè)迭代器完整提供另一個(gè)迭代器,請(qǐng)考慮使用 yield from。在此例中,將完整使用所提供的迭代器。如果它是一個(gè)無(wú)限迭代器,那么外部迭代器也將是無(wú)限迭代器,并且無(wú)限地繼續(xù)執(zhí)行 yield from 語(yǔ)句。
 

itertools.chain 的變體


要注意的最后一點(diǎn)是完整性:如前所述,您不能將 zigzag_iters 函數(shù)與 itertools.chain 一起使用,因?yàn)檫@會(huì)嘗試創(chuàng)建一個(gè)無(wú)限的函數(shù)自變量集合。itertools 背后的理念也考慮到了這一點(diǎn),并提供了一個(gè)可行的變體 itertools.chain.from_iterable。

>>> it = itertools.chain.from_iterable(zigzag_iters(2))
>>> next(it)
0
>>> next(it)
1
>>> next(it)
2
>>> next(it)
1
>>> next(it)
0
>>> next(it)
1
>>> next(it)
2
>>> next(it)
1
>>> next(it)
0
>>> next(it)
1
>>> next(it)
2
>>> next(it)
1

itertools 中提供的其他內(nèi)容


您可在 itertools 中找到一些可用于處理無(wú)限迭代器的簡(jiǎn)單例程。其中之一便是 itertools.islice。您可以使用此例程從迭代器(包括從無(wú)限迭代器)中抽取子集序列。

>>> it1 = zigzag(5)
>>> it2 = itertools.islice(it1, 0, 20)
>>> list(it2)
[0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 4, 3, 2, 1]
>>> it2 = itertools.islice(it1, 1, 20)
>>> list(it2)
[1, 2, 3, 4, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 4, 3, 2, 1]
>>> it2 = itertools.islice(it1, 0, 20, 2)
>>> list(it2)
[0, 2, 4, 4, 2, 0, 2, 4, 4, 2]

該函數(shù)會(huì)接受要摘錄的迭代器、開(kāi)始索引和結(jié)束索引。您還可以選擇提供步長(zhǎng),在某種程度上,它類(lèi)似于 range。但要注意:與 range 不同,它不允許使用負(fù)步長(zhǎng)值。

分組

這是 itertools 中最有用也是最復(fù)雜的部分之一,非常值得關(guān)注。

使用 itertools.groupby,根據(jù)某些條件對(duì)迭代器進(jìn)行分段。以下示例將小時(shí)分組為十二個(gè)小時(shí)周期象限。

>>> hours = range(12)
>>> def divide_by_3(n): return n//3
... 
>>> it = itertools.groupby(hours, divide_by_3)
>>> next(it)
(0, <itertools._grouper object at 0x1075ac9e8>)

您為 itertools.groupby 提供了一個(gè)輸入迭代器和一個(gè)分組函數(shù),將針對(duì)該迭代器中的每個(gè)項(xiàng)執(zhí)行此函數(shù)。當(dāng)分組函數(shù)的結(jié)果從一個(gè)項(xiàng)更改為下一個(gè)項(xiàng)時(shí),將創(chuàng)建一個(gè)新組,其中包含所有項(xiàng),直到下一次更改分組函數(shù)值。分組函數(shù) divide_by_3 使用取整除法運(yùn)算符 // 來(lái)執(zhí)行除法,然后向下舍入到下一個(gè)最小整數(shù)。

圖 1 有助于直觀展示此示例中 itertools.groupby 的運(yùn)行過(guò)程。
 

圖 1. 直觀展示 itertools.groupby
 

理解 itertools.groupby 的關(guān)鍵是記住分組函數(shù)值的變化是導(dǎo)致輸出中的分組項(xiàng)的原因。通過(guò)一些練習(xí),您將能夠熟練地為各種用例編寫(xiě)分組函數(shù)。

讓我們更深入地研究一下 itertools.groupby 示例。

>>> hours = range(12)
>>> for quadrant, group_it in itertools.groupby(hours, divide_by_3): 
...print('Items in quadrant', quadrant)
...for i in group_it: 
...print(i)
... 
Items in quadrant 0
0
1
2
Items in quadrant 1
3
4
5
Items in quadrant 2
6
7
8
Items in quadrant 3
9
10
11

此處,外部循環(huán)調(diào)用 itertools.groupby,但循環(huán)中的每一項(xiàng)都是一個(gè)元組。此元組中的第二個(gè)元素 group_it 又是一個(gè)迭代器,如圖 1 中所示。因此,內(nèi)部循環(huán)適用于每個(gè)組的迭代器。
 

使用無(wú)限迭代器進(jìn)行分組


要全面掌握本教程中的幾個(gè)概念,您可以將分組與無(wú)限迭代器結(jié)合使用。首先看一下 itertools 中的一個(gè)功能,它可巧妙地創(chuàng)建無(wú)限迭代器。

>>> inf_it = itertools.cycle(range(12))
>>> print(list(itertools.islice(inf_it, 32)))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7]

在第一行中,我使用了 itertools.cycle,它通過(guò)您提供的有限迭代器來(lái)創(chuàng)建無(wú)限循環(huán)。在此例中,它只是反復(fù)生成 0 到 11 這個(gè)序列。我使用了 itertools.islice 來(lái)安全地接受它的一個(gè)子集并將其顯示為列表。我還使用了 itertools.repeat,它接受一個(gè)值并反復(fù)迭代該值。

本教程要探索的最后一個(gè)工具是結(jié)合使用 itertools.groupbyitertools.cycle。

>>> inf_it = itertools.cycle(range(12))
>>> hours_32 = itertools.islice(inf_it, 32)
>>> for quadrant, group_it in itertools.groupby(hours_32, divide_by_3): 
...print('Items in quadrant', quadrant, ':', list(group_it))
... 
Items in quadrant 0 : [0, 1, 2]
Items in quadrant 1 : [3, 4, 5]
Items in quadrant 2 : [6, 7, 8]
Items in quadrant 3 : [9, 10, 11]
Items in quadrant 0 : [0, 1, 2]
Items in quadrant 1 : [3, 4, 5]
Items in quadrant 2 : [6, 7, 8]
Items in quadrant 3 : [9, 10, 11]
Items in quadrant 0 : [0, 1, 2]
Items in quadrant 1 : [3, 4, 5]
Items in quadrant 2 : [6, 7]

仔細(xì)研究本課程,重點(diǎn)掌握這些迭代器工具的交互方式。例如,您可以查看小時(shí)數(shù)的循環(huán)如何導(dǎo)致分組函數(shù)結(jié)果的循環(huán)。

您可以開(kāi)始了解如何將基本構(gòu)建塊緊湊地組合在一起以形成有趣的迭代器模式。當(dāng)您將此類(lèi)工具與自己的專(zhuān)用生成器結(jié)合使用時(shí),將會(huì)成為一個(gè)強(qiáng)大的范例,可以高效地解決連續(xù)處理數(shù)據(jù)時(shí)遇到的問(wèn)題。
 

前進(jìn)并迭代


itertools 的內(nèi)容遠(yuǎn)不止我在本教程中介紹的那些,但是您現(xiàn)在已經(jīng)基本了解了它的一些工具,并且很好地了解了其中最復(fù)雜但可能最有價(jià)值的工具 groupby。

與完整標(biāo)準(zhǔn)庫(kù)一樣,您還可以找到該模塊的完整文檔,但它通常是供資深開(kāi)發(fā)人員查找詳細(xì)信息的。并非為初學(xué)者設(shè)計(jì)。我建議您從本教程和上一個(gè)教程中的構(gòu)建塊開(kāi)始,逐步試用 itertools 的功能以及內(nèi)置功能和標(biāo)準(zhǔn)庫(kù)中可用于迭代器的其他工具。

在本系列的第 3 部分中,走向荒無(wú)人煙之地。您將探索協(xié)程,這是一種特殊的生成器函數(shù)。您還將了解另一個(gè)功能強(qiáng)大但十分復(fù)雜的標(biāo)準(zhǔn)庫(kù)模塊:asyncio.

標(biāo)簽: 安全 代碼

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請(qǐng)聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請(qǐng)與原作者聯(lián)系。

上一篇:對(duì)比美英與我國(guó)數(shù)據(jù)科學(xué)教育戰(zhàn)略、現(xiàn)狀

下一篇: 一文盤(pán)點(diǎn)2012年以來(lái)國(guó)內(nèi)大數(shù)據(jù)相關(guān)政策