技术赋能#3:Python可迭代对象的相关函数(2)

除了上一篇文章介绍的Python的内置函数,Python还在其标准库itertools中提供了许多用于处理迭代器的便利函数。本文将介绍其中几个函数,关于完整列表及其使用方法请参见 Python 官方文档 - itertools

因为itertools是Python标准库的一部分,因此无需额外的操作,直接import即可引入该包(这里为了便于演示导入了itertools内的所有变量*,但是在程序中不建议这样做):

1
>>> from itertools import *

accumulate 函数

accumulate 函数会对逐个累加输入的迭代器的数据,其返回值也是一个迭代器,每步返回当前元素与之前所有元素的累加和。此外,可以通过func和initial参数指定累加使用的函数与初始值:

1
2
3
4
5
>>> print(*accumulate(range(1, 11))) # 累加
1 3 6 10 15 21 28 36 45 55
>>> import operator
>>> print(*accumulate(range(1, 11), func = operator.mul, initial = 1)) # 累乘
1 2 6 24 120 720 5040 40320 362880 3628800 # 注意这里会首先返回initial的值

chain 函数

顾名思义,chain函数可以将多个可迭代对象像锁链一样串在一起:

1
2
>>> print(*chain(range(5), "abcde", [1,3,5,7,9]))
0 1 2 3 4 a b c d e 1 3 5 7 9

pairwise 函数

pairwise会将可迭代对象每一步的返回值与其前一步的值组成一个元组:

1
2
>>> print(*pairwise(range(7)))
(0, 1) (1, 2) (2, 3) (3, 4) (4, 5) (5, 6)

例如我们在编程时会遇到求列表中相邻元素之差的情况,比较朴素的实现如下(不考虑边界条件):

1
2
3
4
5
def delta1(lst):
delta = []
for i in range(len(lst)-1):
delta.append(lst[i+1]-lst[i])
return delta

使用zip函数可以写成如下形式:

1
2
def delta2(lst):
return [j-i for i,j in zip(lst, lst[1:])]

当输入是一般的可迭代对象时(无法创建切片),则可以使用pairwise函数:

1
2
def delta3(iterable):
return [j-i for i,j in pairwise(iterable)]

product 函数

product函数会返回两个或多个可迭代对象的笛卡尔积,

1
2
>>> print(*product(range(3), range(5,7)))
(0, 5) (0, 6) (1, 5) (1, 6) (2, 5) (2, 6)

例如下面这个三层循环

1
2
3
4
5
for i in range(10):
for j in "abcdefg":
for k in "+-*/":
value = (i, j, k)
"""do something with value"""

使用product函数就可以改写为这个形式,起到了压缩嵌套循环的效果:

1
2
for value in product(range(10), "abcdefg", "+-*/"):
"""do something with value"""

permutations 函数

该函数会返回输入集合的所有长度为r的排列,例如:

1
2
>>> print(*permutations([1,2,3], r=3))
(1, 2, 3) (1, 3, 2) (2, 1, 3) (2, 3, 1) (3, 1, 2) (3, 2, 1)

这比写三层循环简洁了许多。

与其相似的还有 combinations 函数与 combinations_with_replacement 函数,分别为无放回的和有放回的组合函数。

注:如果你只是想求排列数 AknA^n_k 或者组合数 CknC^n_k ,使用上述三个函数加len()就有些没必要了(因为会浪费内存与计算时间)。对于这种情况,math 库提供了 perm 函数与 comb 函数,可以直接根据公式求值。

takewhile 与 dropwhile 函数

takewhile函数会逐个返回输入的可迭代对象的值,直到测试条件为假,例如:

1
2
>>> print(*takewhile(lambda x: x<3, [1,2,3,2,1]))
1 2

这个函数可以理解为与break具有相同的功能,即提前结束循环,去除多余的计算步骤。

类似的还有dropwhile函数,可以视为相反的takewhile,它会在测试条件首次为假时开始传递输入的可迭代对象的值:

1
2
>>> print(*dropwhile(lambda x: x<3, [1,2,3,2,1]))
3 2 1

本文介绍了Python的itertools标准库提供的七个函数,在下一篇技术博客中我们将介绍迭代器与Python函数式编程的关系,并给出一些Python函数式编程的实例。