关于python:itertools-高效迭代器│Python标准库

4次阅读

共计 8045 个字符,预计需要花费 21 分钟才能阅读完成。

前情提醒: 测试代码中,右尖括号(>)表示命令行中输出的命令;独自一行并以井字符(#)结尾的为输入内容;库的导入仅在本文的第一个测试代码中展示,其余代码块均省略库的导入代码。

  • 零碎类型: Windows 10
  • python 版本: Python 3.9.0

itertools 模块标准化了一个疾速、高效利用内存的外围工具集,次要用来实现一系列迭代器。这些工具函数自身或工具函数之间的组合都是十分有用的。它们一起造成了 迭代器代数

itertools 模块中所有的函数都是创立并返回一个迭代器。它们的区别就是迭代器生成的内容,以及应用场景。

无穷迭代器

无穷迭代器最大的特点是可有限迭代元素,所以在应用时要留神肯定要有限度条件来管制迭代器的进行,否则将造成死循环。

itertools.count(start=0, step=1)
    start: int | float, 起始值
    step: int | float, 步长

创立一个迭代器,第一个元素的值为参数 start 的值,之后元素的值依照参数 step 的值顺次累加。

import itertools

'''打印一下迭代器自身'''
print(itertools.count(7, 1))
# count(7)

'''迭代器类型'''
print(type(itertools.count(7, 1)))
# <class 'itertools.count'>

'''一个简略的小利用'''
for i in itertools.count(7, 1):
    print(i)
    if i > 10:
        break
# 7
# 8
# 9
# 10
# 11

itertools 模块中应用函数返回的迭代器的类型与函数同名,可间接在应用迭代器的场景应用。

itertools.cycle(iterable)
iterable: 可迭代对象

创立一个迭代器,以传入的迭代器中的元素为根底,返回这些元素,当这些元素被取完时,会从新从第一个元素获取,有限反复。

'''小利用, 当取到第三个 A 时进行'''
test_str = ''for i in itertools.cycle('ABC'):
    test_str += i
    if test_str.count('A') >= 3:
        break
print(test_str)
# ABCABCA
itertools.repeat(object[, times])
object: python 对象, 能够是任何数据
times: int, 迭代次数, 可选参数, 默认为有限次

创立一个迭代器,一直反复 object。若设定了 times,则最多反复 times 次,否则将反复有限次。通常用于 map() 函数中当作其中一个参数。

'''无限的迭代器'''
print(list(itertools.repeat(10, 3)))
# [10, 10, 10]

'''应用 map() 函数将两个迭代器元素之间的 pow() 后果收集起来'''
print(list(map(pow, range(10), itertools.repeat(2))))
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

以上这些无穷迭代器在测试时都是应用本身条件或内部限度来使它迭代无限的次数,这些函数生成的迭代器通常用于不确定要迭代次数的场景。

迭代器的操作与解决

itertools.accumulate(iterable[, func, *, initial=None])
iterable: 可迭代对象
func: 带有两个参数的函数, 可选参数
initial: 关键字参数, 默认为 None, 若此参数传参, 则此参数作为 iterable 的第一个元素

创立一个迭代器,默认状况下返回累计汇总值,若传入 func,则依照 func 中的办法执行。例如: 若 iterable[1, 2, 3],则返回的迭代器为 [1, 1+2, 1+2+3]

'''默认状况, 返回累计汇总值'''
print(list(itertools.accumulate([1, 2, 3, 4, 5])))
# [1, 3, 6, 10, 15]

'''办法批改为计算累计的乘积'''
print(list(itertools.accumulate([1, 2, 3, 4, 5], lambda x, y: x * y)))
# [1, 2, 6, 24, 120]

'''在迭代器起始地位增加一个元素, 办法不变, 返回累计汇总值'''
print(list(itertools.accumulate([1, 2, 3, 4, 5], initial=100)))
# [100, 101, 103, 106, 110, 115]

'''在迭代器起始地位增加一个元素, 办法批改为计算累计的乘积'''
print(list(itertools.accumulate([1, 2, 3, 4, 5], lambda x, y: x * y, initial=10)))
# [10, 10, 20, 60, 240, 1200]
itertools.chain(*iterables)
*iterables: 多个可迭代对象, 以逗号隔开

创立一个迭代器,将多个可迭代对象的元素增加到迭代器中。

'''将多个可迭代对象整合成一个迭代器'''
temp = itertools.chain([1, 2, 3], ('A', 'B', 'C'), {'一', '二', '三'})
print(list(temp))
# [1, 2, 3, 'A', 'B', 'C', '一', '三', '二']
itertools.chain.from_iterable(iterable)
iterable: 可迭代对象

itertools.chain() 函数相似,然而参数是一个可迭代对象,将这个可迭代对象中元素一一增加到新迭代器中,如果元素是一个可迭代对象,那么会将这个元素内的元素一一增加到新迭代器中。小编本人的了解就是迭代器降维。

'''将二维迭代器降维'''
temp = itertools.chain.from_iterable(['1', ['2', '3'], ('4', '5')])
print(list(temp))
# ['1', '2', '3', '4', '5']

'''只能降一层维度, 三维将至二维'''
temp = itertools.chain.from_iterable(['1', ['2', '3'], ('4', ['5', '6'])])
print(list(temp))
# ['1', '2', '3', '4', ['5', '6']]

PS: 迭代器维度概念能够了解为,整个迭代器中的元素都不是迭代器类型的就是 一维 ,迭代器中的元素有是迭代器的就是 二维 ,迭代器中的元素有是迭代器的,而后这个迭代器中还有元素是迭代器的就是 三维,依此类推。

itertools.compress(data, selectors)
data: 可迭代对象, 蕴含所需的数据
selectors: 可迭代对象, 真值测试数据

创立一个迭代器,将 data 中通过 selectors 真值测试为 True 的元素保留。当两个可迭代对象中的某一个达到开端时执行进行,返回最终后果。

'''只判断前三个元素, 并且索引值为 0 和 2 的元素会保留到新迭代器中并返回'''
temp = itertools.compress(['A', 'B', 'C', 'D'], [1, 0, 1])
print(list(temp))
# ['A', 'C']
itertools.dropwhile(predicate, iterable)
predicate: 只须要一个参数的函数
iterable: 可迭代对象

创立一个迭代器,顺次依据 perdicate 函数计算 iterable 中的元素,当计算结果第一次为 false 时,此元素之前的元素全副摈弃,残余的 (蕴含计算结果为正数的这个元素) 保留至新迭代器中。

此函数理论运行流程为: 计算第一个元素,若为 True 则不返回,而后计算下一个元素,若都为 True 则都不返回,直到第 N 个元素的计算结果为 False,这时返回这个元素,尔后的所有元素都不进行计算了,间接返回。

'''真值测试'''
temp = itertools.dropwhile(bool, [1, 2, 0, 3])
print(list(temp))
# [0, 3]

'''元素的值减 2 小于等于 0 的为 True'''
temp = itertools.dropwhile(lambda x: x - 2 <= 0, [1, 2, 0, 3, 1])
print(list(temp))
# [3, 1]
itertools.takewhile(predicate, iterable)
predicate: 只须要一个参数的函数
iterable: 可迭代对象

创立一个迭代器,将 iterable 中的元素当作 function 的参数计算,与 dropwhile() 函数恰恰相反,当后果的布尔值为 True 时,其元素增加到新迭代器中, 直到有元素的计算结果为 False 时,此元素与之后的元素全副摈弃。

temp = itertools.takewhile(bool, [1, 3, None, 0, 1])
print(list(temp))
# [1, 3]
itertools.filterfalse(predicate, iterable)
predicate: 只须要一个参数的函数
iterable: 可迭代对象

创立一个迭代器,仅保留 iterable 中在 predicate 计算中为 False 的元素。如果 predicate 传入的是 None,则相当于传入 bool,意思是做真值测试。

'''元素的值减 2 小于等于 0 的为 True'''
temp = itertools.filterfalse(lambda x: x - 2 <= 0, [1, 2, 0, 3, 1])
print(list(temp))
# [3]

'''真值测试'''
temp = itertools.filterfalse(None, [1, 2, 0, 3, 1])
print(list(temp))
# [0]
itertools.groupby(iterable, key=None)
iterable: 可迭代对象
key: 执行函数

创立一个迭代器,将 iterable 的元素顺次通过 key 计算出后果,每种后果别离创立一个列表,后果作为列表的第一个元素,所有后果为这个数值的元素组成一个迭代器作为列表的第二个元素,最初这些列表保留至新迭代器中并返回。

在返回的迭代器中,列表内的第二个元素也是迭代器,类型与返回的迭代器统一,并且是共享底层的可迭代对象。因为源是共享的,所以当对返回的迭代器进行操作时,总是会失去空值,所以如果须要在之后的程序中应用这些数据,那么就要手动保留到列表或其余地位。

'''真值测试'''
groups = []
for k, g in itertools.groupby([0, 3, 2, 1, 4], bool):
    groups.append((k, list(g)))

print(groups)
# [(False, [0]), (True, [3, 2, 1, 4])]
itertools.islice(iterable, start, stop[, step])
iterable: 可迭代对象
start: 起始地位
stop: 完结地位
step: 步长

创立一个迭代器,返回从 iterable 中被选中的元素,被选中的元素从 start 索引地位开始,以 step 为步长,直到 stop 索引地位完结。相似列表、字符串等的切片操作。

在应用 itertools.islice() 函数时, 还能够只传入 iterablestop 两个参数。

若仅这两个参数,则 start 默认为 0step 默认为 1. 若 stop 参数传入 None,则默认为直到迭代器开端。startstopstep 三个参数均不反对正数。

iterable = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

'''完结地位 7'''
print(list(itertools.islice(iterable, 7)))
# [0, 1, 2, 3, 4, 5, 6]

'''起始地位 1, 完结地位 7'''
print(list(itertools.islice(iterable, 1, 7)))
# [1, 2, 3, 4, 5, 6]

'''起始地位 1, 完结地位到开端'''
print(list(itertools.islice(iterable, 1, None)))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

'''起始地位 1, 完结地位 7, 步长 2'''
print(list(itertools.islice(iterable, 1, 7, 2)))
# [1, 3, 5]
itertools.starmap(function, iterable)
function: 函数
iterable: 可迭代对象

创立一个迭代器,将 iterable 中的元素当作 function 的参数计算,并将后果保留在新迭代器中。iterable 中的每个元素都是元组,元组内蕴含了 function 所需的参数。

'''失去同样后果的 starmap() 和 map()'''
temp = itertools.starmap(pow, [(1, 2), (3, 4), (5, 6)])
print(list(temp))
# [1, 81, 15625]

print(list(map(pow, (1, 3, 5), (2, 4, 6))))
# [1, 81, 15625]
itertools.tee(iterable, n=2)
iterable: 可迭代对象
n: 默认值为 2, 决裂的迭代器数量

从一个可迭代对象中决裂出 n 个独立的可迭代对象并返回。

for i in itertools.tee([1, 2, 3, 4, 5, 6], 2):
    print(list(i))
# [1, 2, 3, 4, 5, 6]
# [1, 2, 3, 4, 5, 6]
itertools.zip_longest(*iterables, fillvalue=None)
*iterables: 一个或多个可迭代对象
fillvalue: 关键字参数, 填充值, 默认为 None

创立一个迭代器,从所有传入的可迭代对象中抽取一个元素,将它们组合成一个元组,而后增加到迭代器中。当传入的可迭代对象中的元素被读取完,并且还有其余的可迭代对象的元素并未读取完时,将依据参数 fillvalue 的值填补缺失。当所有传入的可迭代对象被读取结束后,返回新迭代器。

temp = itertools.zip_longest('xyz', '123456', '暗语寒飞', fillvalue='*')
print(list(temp))
# [('x', '1', '暗'), ('y', '2', '语'), ('z', '3', '寒'), ('*', '4', '飞'), ('*', '5', '*'), ('*', '6', '*')]

排列组合

product(*iterables, repeat=1)
*iterables: 一个或多个可迭代对象
repeat: 关键字参数, 反复次数, 默认为 1

对一个或多个可迭代对象计算笛卡尔积。即为前一个可迭代对象的元素与后一个可迭代对象的元素的所有组合,每个组合生成一个元组并增加到新迭代器中。

参数 repeat 是传入的可迭代对象的反复次数, 例如: product('AB', repeat=2) 等同于 product('AB', 'AB')

temp = itertools.product('ABC', 'XY')
print(list(temp))
# [('A', 'X'), ('A', 'Y'), ('B', 'X'), ('B', 'Y'), ('C', 'X'), ('C', 'Y')]
temp = itertools.product('AB', 'XY', repeat=2)
print(list(temp))
# [('A', 'X', 'A', 'X'), ('A', 'X', 'A', 'Y'), ('A', 'X', 'B', 'X'), ('A', 'X', 'B', 'Y'), 
#  ('A', 'Y', 'A', 'X'), ('A', 'Y', 'A', 'Y'), ('A', 'Y', 'B', 'X'), ('A', 'Y', 'B', 'Y'), 
#  ('B', 'X', 'A', 'X'), ('B', 'X', 'A', 'Y'), ('B', 'X', 'B', 'X'), ('B', 'X', 'B', 'Y'), 
#  ('B', 'Y', 'A', 'X'), ('B', 'Y', 'A', 'Y'), ('B', 'Y', 'B', 'X'), ('B', 'Y', 'B', 'Y')]
permutations(iterable, r=None)
iterable: 可迭代对象
r: 关键字参数, 新元素的长度, 默认为 None, 即为新元素的长度就是元素个数

iterable 中的元素以长度为 r 进行排列。每个排列组合生成一个元组并增加到新迭代器中。

排列时,排列程序是依照 iterable 中元素的程序进行的。因而,未排序的 iterable 和已排序的 iterable 排列后的后果是不同的。

iterable 中有雷同值的元素,然而它们的地位是不同的,排列时也就会会认为它们是不同的。

PS: 排列即为数学上的排列,从 n 个元素中取出 m 个元素的无反复排列或直线排列。

'''将 4 个元素以 2 的长度排列'''
temp = itertools.permutations('ABCD', 2)
print(list(temp))
# [('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'A'), ('B', 'C'), ('B', 'D'), ('C', 'A'), ('C', 'B'), ('C', 'D'), ('D', 'A'), ('D', 'B'), ('D', 'C')

'''将 3 个元素以 3 的长度排列'''
temp = itertools.permutations('ABC')
print(list(temp))
# [('A', 'B', 'C'), ('A', 'C', 'B'), ('B', 'A', 'C'), ('B', 'C', 'A'), ('C', 'A', 'B'), ('C', 'B', 'A')]
combinations(iterable, r)
iterable: 可迭代对象
r: 新元素的长度

iterable 中的元素以长度为 r 进行排列。每个排列组合生成一个元组并增加到新迭代器中。与 permutations() 函数基本一致,然而 combinations() 函数会过滤掉元素值统一的元组。

'''筛选掉元素值一样的元组'''
temp = itertools.combinations('ABC', 2)
print(list(temp))
# [('A', 'B'), ('A', 'C'), ('B', 'C')]

'''对照组'''
temp = itertools.permutations('ABC', 2)
print(list(temp))
# [('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')]
combinations_with_replacement(iterable, r)
iterable: 可迭代对象
r: 关键字参数, 新元素的长度, 默认为 None, 即为新元素的长度就是元素个数

combinations() 函数的变种,在保留过滤元素值统一的元组这个性能的根底上,此函数在排列时元素本人也会与本人所在的可迭代对象进行排列。

'''会与本人所在的可迭代对象排列'''
temp = itertools.combinations_with_replacement('ABC', 2)
print(list(temp))
# [('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'B'), ('B', 'C'), ('C', 'C')]

'''对照组'''
temp = itertools.permutations('ABC', 2)
print(list(temp))
# [('A', 'B'), ('A', 'C'), ('B', 'C')]

参考资料

官网文档: https://docs.python.org/zh-cn…

公众号 :「python 库详解」,专一于 python 规范库与 python 第三方库的解析教程,以及其余 python 的相干内容。挖掘更多原创文章,期待您的关注。

正文完
 0