Python 進階之路 (十) 再立Flag, 社區最全的itertools深度解析(中)

前情回顧

你們好,我又回來了。今天我會繼續和你們分享itertools這個神奇的自帶庫,首先,讓咱們回顧一下上一期結尾的時候咱們講到的3個方法:python

  • combinations()
  • combinations_with_replacement()
  • permutations()

讓咱們對這3個在排列組合中常常會使用到的函數作個總結markdown

combinations()

基礎概念
  • 模板:combinations(iterable, n)
  • 參數:iterable爲可迭代的對象(list,tuple...), n爲想要的組合包含的元素數
  • 返回值: 返回在iterable裏n個元素組成的tuple的所有組合(不考慮順序,元素自身不可重複)
應用實例
import itertools as it
lst = [1,2,3]
result = list(it.combinations(lst,2))
print(result)

Out: [(1, 2), (1, 3), (2, 3)]

這裏咱們從lst這個list裏面選取全部由兩個元素組成的組合,獲得結果如圖所示,這裏沒有考慮順序,所以咱們不會看到(1,2)和(2,1)被算做兩種組合,元素自身不可重複,因此沒有(1,1),(2,2),(3,3)的組合出現app

combinations_with_replacement()

基礎概念
  • 模板:combinations_with_replacement(iterable, n)
  • 參數:iterable爲可迭代的對象(list,tuple...), n爲想要的組合包含的元素數
  • 返回值: 返回在iterable裏n個元素組成的tuple的所有組合(不考慮順序,元素自身可重複)
應用實例
import itertools as it
lst = [1,2,3]
result = list(it.combinations_with_replacement(lst,2))
print(result)

Out: [(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)]

和剛纔的區別是多了(1,1),(2,2),(3,3)的組合,也就是說容許每一個元素本身和本身組合函數

permutations()

基礎概念
  • 模板:permutations(iterable, n=None)
  • 參數:iterable爲可迭代的對象(list,tuple...), n爲想要的組合包含的元素數
  • 返回值: 返回在iterable裏n個元素組成的tuple的所有組合(考慮順序,元素自身不可重複)
應用實例
import itertools as it
lst = [1,2,3]
result = list(it.permutations(lst,2))
print(result)

Out: [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]

咱們用permutations獲得的結果是自身元素不能重複的狀況下,一個iterable裏面由n個元素構成的所有組合,考慮順序優化

不一樣點彙總

咱們這裏能夠簡單彙總一下三個函數的不一樣點,彙總一張精華滿滿的表格送個你們,但願你們若是往後有一天須要用到的話能夠回來我這裏看看,順便給勤勞的博主點個贊也是好的👍code

函數 組合的元素個數 示例 list 輸出結果 特色
combinations() 2 [1,2,3] (1, 2), (1, 3), (2, 3) 元素自身不能重複,不考慮順序
combinations_with_replacement() 2 [1,2,3] (1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3) 元素自身能重複,不考慮順序
permutations() 2 [1,2,3] (1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2) 元素自身不能重複,考慮順序

我必須吐槽一下sf的這個markdown,連個表格的快捷方式都沒有麼。。。。對象

數字序列

在完美的收尾了上一期的內容後咱們能夠繼續前進了,使用itertools,咱們能夠輕鬆地在無限序列上生成迭代器。接下來咱們主要看看和數字序列相關的方法和功能。three

首先咱們來看一個生成奇數偶數的例子,若是不知道itertools的狀況下,咱們利用生成器的解決方案以下:ip

def evens():
    """Generate even integers, starting with 0."""
    n = 0
    while True:
        yield n
        n += 2

def odds():
    """Generate odd integers, starting with 1."""
    n = 1
    while True:
        yield n
        n += 2


evens = evens()
even_numbers = list(next(evens) for _ in range(5))
print(even_numbers)

odds = odds()
odd_numbers = list(next(odds) for _ in range(5))
print(odd_numbers)

Out:[0, 2, 4, 6, 8]
     [1, 3, 5, 7, 9]

如今咱們能夠利用itertools裏面的it.count()方法進行優化:字符串

import itertools as it

evens = it.count(step=2)
even_numbers = list(next(evens) for _ in range(5))
print(even_numbers)

odds = it.count(start=1, step=2)
odd_numbers = list(next(odds) for _ in range(5))
print(odd_numbers)

Out:[0, 2, 4, 6, 8]
     [1, 3, 5, 7, 9]

itertools.count()這個方法主要就是用來計數,默認從0開始,咱們能夠經過設置start關鍵字參數從任何數字開始計數,默認爲0.一樣也能夠設置step關鍵字參數來肯定從count()返回的數字之間的間隔,默認爲1。

再來看其它兩個用到itertools count方法的例子:

>>> count_with_floats = it.count(start=0.5, step=0.75)
>>> list(next(count_with_floats) for _ in range(5))
[0.5, 1.25, 2.0, 2.75, 3.5]

能夠用來計算float類型

>>> negative_count = it.count(start=-1, step=-0.5)
>>> list(next(negative_count) for _ in range(5))
[-1, -1.5, -2.0, -2.5, -3.0]

或是負數也沒有問題

在某些方面,count()相似於內置range()函數,但count()老是返回無限序列。
無限序列的好處在於它不可能徹底迭代

count()函數甚至模擬了內置的enumrate()功能:

import itertools as it
print(list(zip(it.count(), ['a', 'b', 'c'])))

Out:[(0, 'a'), (1, 'b'), (2, 'c')]

其餘有意思的方法

repeat(object, times=1)

首先讓咱們看一下itertools裏面的repeat放法,它的功能是返回一個值的無限序列:

all_ones = it.repeat(1)  # 1, 1, 1, 1, ...
all_twos = it.repeat(2)  # 2, 2, 2, 2, ...

若是咱們但願能夠指定返回序列的長度,咱們能夠在方法的第二個參數加上想要的序列長度便可:

five_ones = it.repeat(1, 5)  # 1, 1, 1, 1, 1
three_fours = it.repeat(4, 3)  # 4, 4, 4
cycle(iterable)

接着估計你可能想到了,那若是咱們想要不斷循環兩個數呢?答案是itertools的cycle方法:

alternating_ones = it.cycle([1, -1])  # 1, -1, 1, -1, 1, -1, ...

若是你想要輸出上面的alternating_ones是不可能的,由於這是一個無限序列,你會收到下面的錯誤:

Traceback (most recent call last):
  File "C:\Users\Desktop\itertools.py", line 48, in <module>
    alternating_ones = list(it.cycle([0, 1]))
MemoryError
accumulate(iterable, func=operator.add)

itertools.accumulate()函數, 這個函數有些特殊,它接受兩個參數 :

  • 一個可迭代的輸入
  • 一個二進制函數func(即一個具備兩個輸入的函數)

並返回一個迭代器,用於將func應用於輸入元素的累積結果。看一個小栗子:

>>> import operator
>>> list(it.accumulate([1, 2, 3, 4, 5], operator.add))
[1, 3, 6, 10, 15]

accumulate()返回的迭代器中的第一個值始終是輸入序列中的第一個值。在這個例子中,是1,由於1是 [1,2,3,4,5]中的第一個值。
輸出迭代器中的下一個值是輸入序列的前兩個元素的總和:add(1,2)= 3,因此操做模式以下:

  • add(3, 3) = add(add(1, 2), 3) = 6

以此類推,最終獲得最後的答案。實際上accumulate()的第二個參數自己就是默認爲operator.add(),所以前面的示例能夠簡化爲:

>>> list(it.accumulate([1, 2, 3, 4, 5]))
[1, 3, 6, 10, 15]

咱們也能夠本身添加別的方法到第二個參數裏:

>>> list(it.accumulate([9, 21, 17, 5, 11, 12, 2, 6], min))
[9, 9, 9, 5, 5, 5, 2, 2]

好啦,itertools 有關於數字序列方面的方法我就簡單介紹到這裏啦,有須要的朋友能夠本身去看看,其實仍是很是實用的,不是相似lambda那些花哨的方法

對List和字符串的相關操做

itertools.product 實現交叉組合
>>> product([1, 2], ['a', 'b'])
(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')

此處實現兩個可迭代序列的元素組合。

itertools.tee 從一個輸入序列生成任意數量的生成器
>>> iter1, iter2 = it.tee(['a', 'b', 'c'], 2)
>>> list(iter1)
['a', 'b', 'c']
>>> list(iter2)
['a', 'b', 'c']

注意這裏的iter1和iter2相互不會一影響。是一個深複製

islice(iterable, stop) islice(iterable, start, stop, step=1) 切片
>>> islice([1, 2, 3, 4], 3)
1, 2, 3

>>> islice([1, 2, 3, 4], 1, 2)
2, 3

這裏和list最大的區別在於返回對象是一個迭代器,並非一個list,islice(iterable, stop)中stop是切片截至的index,和list切片同樣,並不會包括stop自己的值。若是想要指定切片起始位置和不長,就使用islice(iterable, start, stop, step=1)

chain(*iterables)
>>> chain('abc', [1, 2, 3])  #<type 'itertools.chain'> 
'a', 'b', 'c', 1, 2, 3

返回一個鏈對象,其__next __()方法返回第一個iterable中的元素,直到它耗盡,而後是來自下一個iterable的元素,直到全部的iterables都用完爲止。

這裏有一點須要注意,chain()函數有一個類方法.from_iterable(),它將一個iterable做爲參數。迭代的元素自己必須是可迭代的,所以效應是chain.from_iterable()某種程度上能夠實現相似 list.extend() 或者 list.append() 的功能, 咱們一塊兒看一個混合了不少itertools中其餘方法的綜合小栗子:

import itertools as it
cycle = it.chain.from_iterable(it.repeat('abc'))
result = list(it.islice(cycle,8))
print(result)

Out: ['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b']

這裏其實it.chain.from_iterable裏面甚至能夠放進一個無限序列,不必定是定長的。

總結

這一期的內容沒有那麼長,咱們簡單瞭解了一下itertools的基礎好用的方法,下一期就是簡單實戰了,我本身在網上找了一些很是不錯的案例,照貓畫虎練習了一下,打算在下一期和你們一塊兒分享。此次的文章就到這裏啦,咱們下期見!!!

相關文章
相關標籤/搜索