itertools:用于處理可迭代對象的模塊
Python 有一個內置模塊 itertools,從名字可以看出它是專門用來處理可迭代對象的,那么它都支持哪些操作呢?一起來看一下吧。
itertools.chain
接收多個可迭代對象(或者迭代器)作為參數,返回一個迭代器。它會生成所有輸入迭代器的元素,就好像這些元素來自一個迭代器一樣。
import itertools
c = itertools.chain([1, 2, 3], "abc", {"k1": "v1", "k2": "v2"})
# 直接打印的話是一個對象
print(c)
"""
<itertools.chain object at 0x00000000029745F8>
"""
print(list(c))
"""
1 2 3 a b c k1 k2
"""
# 還可以使用 chain.from_iterable
# 參數接收多個可迭代對象組成的一個可迭代對象
c = itertools.chain.from_iterable(
[[1, 2, 3], "abc", {"k1": "v1", "k2": "v2"}]
)
print(list(c))
"""
1 2 3 a b c k1 k2
"""
itertools.zip_longest
從名字上可以看出,功能和內置的 zip 類似。確實如此,就是將多個可迭代對象對應位置的元素組合起來,像拉鏈(zip)一樣。只不過內置的 zip 是 "木桶原理",一方匹配到頭了,那么就不匹配了,而 zip_longest 是以長的那一方為基準。
import itertools
# 內置的 zip 是把多個迭代器對象中的每一個元素按照順序組合到一個元組中
name = ["高老師", "豬哥", "S 佬"]
where = ["江蘇", "北京", "深圳"]
z = zip(name, where)
print(z)
"""
<zip object at 0x00000257F3FEBEC0>
"""
print(list(z))
"""
[('高老師', '江蘇'), ('豬哥', '北京'), ('S 佬', '深圳')]
"""
# 但如果兩者長度不一致怎么辦?
name = ["高老師", "豬哥", "S 佬", "xxx"]
where = ["江蘇", "北京", "深圳"]
print(list(zip(name, where)))
"""
[('高老師', '江蘇'), ('豬哥', '北京'), ('S 佬', '深圳')]
"""
# 可以看到,長度不一致的時候,當一方結束之后就停止匹配
# 如果想匹配長的,那么可以使用 itertools 下面的 zip_longest
print(list(itertools.zip_longest(name, where)))
"""
[('高老師', '江蘇'), ('豬哥', '北京'), ('S 佬', '深圳'), ('xxx', None)]
"""
# 默認使用 None 進行匹配,當然我們也可以指定內容
print(list(itertools.zip_longest(name, where, fillvalue="中國")))
"""
[('高老師', '江蘇'), ('豬哥', '北京'), ('S 佬', '深圳'), ('xxx', '中國')]
"""
itertools.islice
如果一個迭代器里面包含了很多元素,我們只想要一部分的話,可以使用 islice,按照索引從迭代器中返回所選擇的元素,并且得到的還是一個迭代器。
import itertools
num = range(20)
# 選擇 index=5 到 index=10(不包含)的位置
s = itertools.islice(num, 5, 10)
print(list(s)) # [5, 6, 7, 8, 9]
# 選擇開頭到 index=5 的位置
s = itertools.islice(num, 5)
print(list(s)) # [0, 1, 2, 3, 4]
# 選擇從 index=5 到 index=15(不包含)的位置,步長為 3
s = itertools.islice(num, 5, 15, 3)
print(list(s)) # [5, 8, 11, 14]
注意:islice 不支持負數索引,因為不知道迭代器有多長,除非全部讀取,可是那樣的話干嘛不直接轉為列表之后再用切片獲取呢?
之所以使用 islice 這種形式,就是為了在不全部讀取的情況下,也能選擇出我們想要的部分,所以這種方式只支持從前往后,不能從后往前。
itertools.tee
將一個可迭代對象拷貝 n 份。
import itertools
r = [1, 2, 3, 4, 5]
i1, i2 = itertools.tee(r, 2)
print(list(i1)) # [1, 2, 3, 4, 5]
print(list(i2)) # [1, 2, 3, 4, 5]
itertools.count
import itertools
"""
count(start=0, step=1) 返回一個迭代器,負責無限地生成連續的整數
接收兩個參數:起始(默認為0)和步長(默認為1)
等價于:
def count(firstval=0, step=1):
x = firstval
while 1:
yield x
x += step
"""
# 起始值為 5,步長為 2
c1 = itertools.count(5, 2)
print(list(itertools.islice(c1, 5)))
"""
[5, 7, 9, 11, 13]
"""
itertools.cycle
import itertools
"""
cycle(iterable) 返回一個迭代器,會無限重復里面的內容,直到內存耗盡
"""
c2 = itertools.cycle("abc")
print(list(itertools.islice(c2, 10)))
"""
['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c', 'a']
"""
itertools.repeat
import itertools
"""
repeat(obj, times=None),無限重復 obj,除非指定 times
"""
# 重復指定的次數
print(list(itertools.repeat("abc", 3)))
"""
['abc', 'abc', 'abc']
"""
itertools.dropwhile
刪除滿足條件的值,注意:是刪除。
import itertools
l = [1, 2, 3, 4, 5]
drop_l = itertools.dropwhile(lambda x: x < 3, l)
# 依舊返回迭代器
print(drop_l)
"""
<itertools.dropwhile object at 0x000001AD63AD0488>
"""
# 可以看到小于3的都被丟掉了
print(list(drop_l))
"""
[3, 4, 5]
"""
itertools.takewhile
這個和 filter 是一樣的,保留滿足條件的值。
import itertools
l = [1, 2, 3, 4, 5]
take_l = itertools.takewhile(lambda x: x < 3, l)
print(take_l)
"""
<itertools.takewhile object at 0x000001D37F512948>
"""
print(list(take_l))
"""
[1, 2]
"""
filter_l = filter(lambda x: x < 3, l)
print(list(filter_l))
"""
[1, 2]
"""
itertools.compress
提供了另一種過濾可迭代對象元素的方法。
import itertools
condition = [True, False, True, True, False]
data = [1, 2, 3, 4, 5]
print(list(itertools.compress(data, condition)))
"""
[1, 3, 4]
"""
# 除了指定 True 和 False,還可以使用 Python 其它類型的值
# 會以其對應的布爾值作為判斷依據
condition = [1, 0, "x", "x", {}]
print(list(itertools.compress(data, condition)))
"""
[1, 3, 4]
"""
itertools.accumulate
accumulate 處理輸入的序列,得到一個類似于斐波那契的結果。
import itertools
print(list(itertools.accumulate(range(5))))
"""
[0, 1, 3, 6, 10]
"""
print(list(itertools.accumulate("abcde")))
"""
["a", "ab", "abc", "abcd", "abcde"]
"""
# 所以這里的相加還要看具體的含義
try:
print(list(itertools.accumulate([[1, 2], (3, 4)])))
except TypeError as e:
print(e)
"""
can only concatenate list (not "tuple") to list
"""
# 這里就顯示無法將列表和元組相加
# 當然也可以自定義
data = [1, 2, 3, 4, 5]
method = lambda x, y: x * y
print(list(itertools.accumulate(data, method)))
"""
[1, 2, 6, 24, 120]
"""
# 可以看到這里的結果就改變了
itertools.product
product 則是會將多個可迭代對象組合成一個笛卡爾積。
import itertools
print(list(itertools.product([1, 2, 3], [2, 3])))
"""
[(1, 2), (1, 3), (2, 2), (2, 3), (3, 2), (3, 3)]
"""
itertools.permutations
import itertools
data = [1, 2, 3, 4]
print(list(itertools.permutations(data)))
# 根據排列組合,顯然是 A44,總共 4 * 3 * 2 * 1 = 24 種組合
"""
[(1, 2, 3, 4), (1, 2, 4, 3), (1, 3, 2, 4), (1, 3, 4, 2), (1, 4, 2, 3), (1, 4, 3, 2),
(2, 1, 3, 4), (2, 1, 4, 3), (2, 3, 1, 4), (2, 3, 4, 1), (2, 4, 1, 3), (2, 4, 3, 1),
(3, 1, 2, 4), (3, 1, 4, 2), (3, 2, 1, 4), (3, 2, 4, 1), (3, 4, 1, 2), (3, 4, 2, 1),
(4, 1, 2, 3), (4, 1, 3, 2), (4, 2, 1, 3), (4, 2, 3, 1), (4, 3, 1, 2), (4, 3, 2, 1)]
"""
# 結果是 A42,總共 4 * 3 = 12 種組合
print(list(itertools.permutations(data, 2)))
"""
[(1, 2), (1, 3), (1, 4),
(2, 1), (2, 3), (2, 4),
(3, 1), (3, 2), (3, 4),
(4, 1), (4, 2), (4, 3)]
"""
itertools.combinations
permutations 顯然是考慮了順序,相當于排列組合里面 A,而 combinations 只考慮元素是否一致,而不管順序,相當于排列組合里面的 C。
import itertools
# permutations 只要順序不同就看做一種結果
# combinations 則保證只要元素相同就是同一種結果
data = "abcd"
print(list(itertools.combinations(data, 3)))
"""
[('a', 'b', 'c'), ('a', 'b', 'd'), ('a', 'c', 'd'), ('b', 'c', 'd')]
"""
# 如果拿抽小球來作比喻的話,顯然 combinations 是不放回的,也就是不會重復單個的輸入元素
# 但有時候可能也需要考慮包含重復元素的組合,相當于抽小球的時候有放回
# 對于這種情況,可以使用 combinations_with_replacement
print(list(itertools.combinations_with_replacement(data, 3)))
"""
[('a', 'a', 'a'), ('a', 'a', 'b'), ('a', 'a', 'c'), ('a', 'a', 'd'), ('a', 'b', 'b'),
('a', 'b', 'c'), ('a', 'b', 'd'), ('a', 'c', 'c'), ('a', 'c', 'd'), ('a', 'd', 'd'),
('b', 'b', 'b'), ('b', 'b', 'c'), ('b', 'b', 'd'), ('b', 'c', 'c'), ('b', 'c', 'd'),
('b', 'd', 'd'), ('c', 'c', 'c'), ('c', 'c', 'd'), ('c', 'd', 'd'), ('d', 'd', 'd')]
"""
以上就是該模塊的用法,但說實話,感覺大部分都沒啥卵用。