文档章节

Python标准库笔记(10) — itertools模块

j_hao104
 j_hao104
发布于 06/14 15:27
字数 3523
阅读 484
收藏 9
点赞 1
评论 0

itertools 用于更高效地创建迭代器的函数工具。

Python版本 3.x

itertools 提供的功能受Clojure,Haskell,APL和SML等函数式编程语言的类似功能的启发。它们的目的是快速有效地使用内存,并且将它们关联在一起以表示更复杂的基于迭代的算法。

基于迭代器的代码比使用列表的代码提供了更好的内存消耗特性。因为直到数据需要使用时才从迭代器中生成,所有数据不需要同时存储在内存中。这种 “惰性” 的处理模式可以减少大型数据集的交换和其他副作用,从而提高性能。

除了 itertools 中定义的函数之外,本文中的示例还使用了一些内置函数进行迭代。

1.合并和分割

chain() 函数将多个迭代器作为参数,并返回一个迭代器,这样它生成所有输入的内容,就像来自于单个迭代器一样。

from itertools import chain

for i in chain([1, 2, 3], ['a', 'b', 'c']):
    print(i, end=' ')

使用 chain() 可以轻松地处理多个序列,而不需要生成一个更大的序列。

# OutPut
1 2 3 a b c 

如果要组合的迭代不是全部预先显式声明的,或者需要惰性计算的,则可以使用 chain.from_iterable() 来替换 chain()

from itertools import chain

def make_iterables_to_chain():
    yield [1, 2, 3]
    yield ['a', 'b', 'c']

for i in chain.from_iterable(make_iterables_to_chain()):
    print(i, end=' ')
# OutPut
1 2 3 a b c 

Python内置函数 zip() 也是返回一个迭代器,但它是将几个迭代器的元素组合成元组。

for i in zip([1, 2, 3], ['a', 'b', 'c']):
    print(i)

zip() 和本模块中的其他函数一样,返回一个可迭代的对象,每次迭代产生一个值。

# OutPut
(1, 'a')
(2, 'b')
(3, 'c')

但是, 使用 zip() 时当第一个输入迭代器耗尽时,zip() 就会停止。如果要处理所有的输入,即使迭代器产生不同数量的值,那么可以使用 zip_longest()

from itertools import zip_longest

r1 = range(3)
r2 = range(2)

print('使用zip会提前结果迭代:')
print(list(zip(r1, r2)))
print()
print('zip_longest会处理完所有值:')
print(list(zip_longest(r1, r2)))

默认情况下,zip_longest() 会使用 None 来填充缺失位置的值。使用 fillvalue 参数来设置不同的替代值。

# OutPut
使用zip会提前结果迭代:
[(0, 0), (1, 1)]

zip_longest会处理完所有值:
[(0, 0), (1, 1), (2, None)]

islice() 函数返回一个迭代器,用于通过索引返回输入迭代器的指定项。

from itertools import islice

print('Stop at 5:')
for i in islice(range(100), 5):
    print(i, end=' ')
print()

print('Start at 5, Stop at 10:')
for i in islice(range(100), 5, 10):
    print(i, end=' ')
print()

print('By tens to 100:')
for i in islice(range(100), 0, 100, 10):
    print(i, end=' ')
print()

islice() 接收和列表切片相同的参数:startstopstepstartstep 参数是可选的。

# OutPut
Stop at 5:
0 1 2 3 4 
Start at 5, Stop at 10:
5 6 7 8 9 
By tens to 100:
0 10 20 30 40 50 60 70 80 90 

tee() 函数作用是根据单个原始输入返回多个独立的迭代器(默认为两个)。

from itertools import islice, tee

r = islice(range(10), 5)
i1, i2 = tee(r)

print('i1:', list(i1))
print('i2:', list(i2))

tee() 具有与Unix tee 实用程序类似的语义,它从它的输入中重复地读取的值并将它们写入一个命名文件和标准输出。 通过 tee() 函数可以将同一组数据提供给多个算法并行处理。

# OutPut
i1: [0, 1, 2, 3, 4]
i2: [0, 1, 2, 3, 4]

需要注意,由 tee() 创建的新迭代器将共享它们的输入,因此在创建新迭代器后不要再使用输入的迭代器。

from itertools import islice, tee

r = islice(range(10), 5)
i1, i2 = tee(r)

print('迭代原始:', end=' ')
for i in r:
    print(i, end=' ')
    if i > 1:
        break
print()

print('i1:', list(i1))
print('i2:', list(i2))

如果原始迭代器已经消耗了一些值,那么新的迭代器将不会生成这些值。

# OutPut
迭代原始: 0 1 2
i1: [3, 4]
i2: [3, 4]

2.计算输入

Python内置的 map() 函数返回一个迭代器。 该迭代器根据输入迭代器中的值调用函数,并返回结果。当任意一个输入迭代器耗尽时它就立刻停止。

def times_two(x):
    return 2 * x

def multiply(x, y):
    return (x, y, x * y)

print('单个输入:')
for i in map(times_two, range(5)):
    print(i, end=' ')

print('\n多个输入:')
r1 = range(5)
r2 = range(5, 10)
for i in map(multiply, r1, r2):
    print('{:d} * {:d} = {:d}'.format(*i))

print('\n迭代停止:')
r1 = range(5)
r2 = range(2)
for i in map(multiply, r1, r2):
    print(i)

在第一个例子中,函数将所有输入值乘以2。在第二个例子中,函数将从两个单独的迭代器中获取的两个参数相乘,并返回一个包含原始参数和计算值的元组。第三个例子中,在生成了两个元组之后便停止了,因为第二个输入已经耗尽。

# OutPut
单个输入:
0 2 4 6 8
多个输入:
0 * 5 = 0
1 * 6 = 6
2 * 7 = 14
3 * 8 = 24
4 * 9 = 36

迭代停止:
(0, 0, 0)
(1, 1, 1)

starmap() 函数与 map() 类似,但不是从多个迭代器构造元组,而是使用 * 语法将单个迭代器中的项作为参数解包给map函数。

from itertools import starmap

values = [(0, 5), (1, 6), (2, 7), (3, 8), (4, 9)]

for i in starmap(lambda x, y: (x, y, x * y), values):
    print('{} * {} = {}'.format(*i))

如果使用 map() 函数将是这种调用 f(i1,i2) ,而使用 starmap() 直接是 f(*i)

#OutPut
0 * 5 = 0
1 * 6 = 6
2 * 7 = 14
3 * 8 = 24
4 * 9 = 36

3.产生新值

count() 函数会返回一个可以无限地产生连续整数的迭代器。第一个数字可以作为参数传递(默认值为0)。没有上限参数(有关对结果集的更多控制,请参阅内置的 range())。

from itertools import count

for i in zip(count(1), ['a', 'b', 'c']):
    print(i)

此示例因为使用了 zip() 和有限长度列表参数所以才停止。

# OutPut
(1, 'a')
(2, 'b')
(3, 'c')

count() 的start和step参数可以是任何可以加在一起的数字值。

import fractions
from itertools import count

start = fractions.Fraction(1, 3)
step = fractions.Fraction(1, 3)

for i in zip(count(start, step), ['a', 'b', 'c']):
    print('{}: {}'.format(*i))

本例中,起始点和步长来自 Fraction (分数)模块的 fraction 对象。

# OutPut
1/3: a
2/3: b
1: c

cycle() 函数的作用是:返回一个迭代器,该迭代器重复无限地给出的参数的内容。因为它必须记住输入迭代器的全部内容,所以如果迭代器很长,它可能会消耗相当多的内存。

from itertools import cycle

for i in cycle(['a', 'b', 'c']):
    print(i)

如果没有打断,它会无限循环下去。

# OutPut
a
b
c
a
b
...

repeat() 函数的作用是:返回一个迭代器,该迭代器每次访问时都会产生相同的值。

from itertools import repeat

for i in repeat('over-and-over', times=5):
    print(i)

repeat() 返回的迭代器将不断返回数据,除非提供可选的times参数来限制次数。

# OutPut
over-and-over
over-and-over
over-and-over
over-and-over
over-and-over

当需要将某个固定值包含在其他迭代器的值中时,使用 repeat()zip()map() 组合会很有用。

from itertools import repeat, count

for i, s in zip(count(), repeat('over-and-over', 5)):
    print(i, s)

在本例中,count值与 repeat() 返回的常量组合在一起。

此示例使用 map() 将从0到4的数字乘以2。

from itertools import repeat

for i in map(lambda x, y: (x, y, x * y), repeat(2), range(5)):
    print('{:d} * {:d} = {:d}'.format(*i))

本例中 repeat() 不需要显式限制迭代次数,因为 range() 只返回五个元素, map() 在其任意输入结束时会停止处理。

# OutPut
2 * 0 = 0
2 * 1 = 2
2 * 2 = 4
2 * 3 = 6
2 * 4 = 8

4.过滤

dropwhile() 函数的作用是:返回一个迭代器,直到条件第一次为false时,该迭代器开始才产生输入迭代器的元素。

from itertools import dropwhile

def should_drop(x):
    print('输入:', x)
    return x < 1

for i in dropwhile(should_drop, [-1, 0, 1, 2, -2]):
    print('产出:', i)

dropwhile() 不会过滤每个输入项; 当第一次条件为假后,便直接返回输入中的所有剩余项目。

# OutPut
输入: -1
输入: 0
输入: 1
产出: 1
产出: 2
产出: -2

dropwhile() 相反的是 takewhile() 。它返回一个迭代器,只要测试函数返回true, 该迭代器就返回输入迭代器中的项目。

from itertools import takewhile

def should_take(x):
    print('输入:', x)
    return x < 1

for i in takewhile(should_take, [-1, 0, 1, 2, -2]):
    print('产生:', i)

一旦should_take()返回 False, takewhile()就停止处理输入。

# OutPut
输入: -1
产生: -1
输入: 0
产生: 0
输入: 1

Python内置函数 filter() 是返回一个包含测试函数返回true的所有项的迭代器。

def check_item(x):
    print('输入:', x)
    return x < 1

for i in filter(check_item, [-1, 0, 1, 2, -2]):
    print('产出:', i)

filter() 不同于 dropwhile()takewhile() 的是,filter() 每个项目在返回之前都代入测试函数。

# OutPut
输入: -1
产出: -1
输入: 0
产出: 0
输入: 1
输入: 2
输入: -2
产出: -2

filterfalse() 返回一个迭代器,该迭代器只包含测试函数返回false的项。

from itertools import filterfalse

def check_item(x):
    print('输入:', x)
    return x < 1

for i in filterfalse(check_item, [-1, 0, 1, 2, -2]):
    print('产出:', i)

测试函数 check_item() 和上例中的一样,但是返回的结果正好和 filter() 相反。

# OutPut
输入: -1
输入: 0
输入: 1
产出: 1
输入: 2
产出: 2
输入: -2

compress() 提供了另一种过滤可迭代内容的方法。它不再是调用函数,而是使用另一个迭代中的值来指示何时接受值何时忽略值。

from itertools import compress, cycle

every_third = cycle([False, False, True])
data = range(1, 10)

for i in compress(data, every_third):
    print(i, end=' ')

compress() 的第一个参数是需要进行处理的可迭代数据,第二个参数是可迭代的生成的布尔值选择器,指示从数据输入中取出哪些元素(True产生值,False忽略)。

# OutPut
3 6 9

5.聚合

groupby() 函数返回一个迭代器,该迭代器生成由公共键聚合的值集。下面例子展示基于属性对相关值进行分组。

from itertools import groupby
import functools
import operator
import pprint


@functools.total_ordering
class Point:

    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return '({}, {})'.format(self.x, self.y)

    def __eq__(self, other):
        return (self.x, self.y) == (other.x, other.y)

    def __gt__(self, other):
        return (self.x, self.y) > (other.x, other.y)


# 生成Point实例的数据集
data = list(map(Point, [1, 2, 3, 1, 2], range(5)))
print('Data:')
pprint.pprint(data, width=35)
print()

# 对无序的data基于属性x聚合
print('聚合, 无序data:')
for k, g in groupby(data, operator.attrgetter('x')):
    print(k, list(g))
print()

# 对data排序
data.sort()
print('排序后:')
pprint.pprint(data, width=35)
print()

# 对排序后的data基于属性X聚合
print('聚合, 有序data:')
for k, g in groupby(data, operator.attrgetter('x')):
    print(k, list(g))

输入序列需要根据键值进行排序处理后才输出预期的聚合结果。

# OutPut
Data:
[(1, 0),
 (2, 1),
 (3, 2),
 (1, 3),
 (2, 4)]

聚合, 无序data:
1 [(1, 0)]
2 [(2, 1)]
3 [(3, 2)]
1 [(1, 3)]
2 [(2, 4)]

排序后:
[(1, 0),
 (1, 3),
 (2, 1),
 (2, 4),
 (3, 2)]

聚合, 有序data:
1 [(1, 0), (1, 3)]
2 [(2, 1), (2, 4)]
3 [(3, 2)]

6.组合

accumulate() 函数的作用是:处理可迭代的输入,将第n和n+1项传递给目标函数,生成返回值,而不是直接返回输入。默认函数功能是将两个值相加,因此可以使用 accumulate() 来生成一系列数值输入的累积和。

from itertools import accumulate

print(list(accumulate(range(5))))
print(list(accumulate('abcde')))

如果输入序列是非整数值时,结果取决于将两个项“相加”在一起的含义。比如上面例子中的第二项 accumulate() 接收的是一个字符串,返回则是将字符串逐个拼接在一起。

# OutPut
[0, 1, 3, 6, 10]
['a', 'ab', 'abc', 'abcd', 'abcde']

同时 accumulate() 也接受自定义的带有两个输入项的函数。

from itertools import accumulate

def f(a, b):
    print(a, b)
    return b + a

print(list(accumulate('abcde', f)))
# OutPut
a b
ba c
cba d
dcba e
['a', 'ba', 'cba', 'dcba', 'edcba']

如果嵌套for循环遍历多个序列可以使用 product() ,它会生成一个迭代器,其值是该组输入值的笛卡尔乘积。

from itertools import product

char = ['a', 'b', 'c']
integer = [1, 2, 3]

for each in product(char, integer):
    print(each)

product() 产生的值是元组,由每个迭代中取出的成员按照它们传递的顺序作为参数传入。

# OutPut
('a', 1)
('a', 2)
('a', 3)
('b', 1)
('b', 2)
('b', 3)
('c', 1)
('c', 2)
('c', 3)

如果要计算序列与其本身的笛卡尔积,则需要指定 repeat 参数。

from itertools import product

char = ['a', 'b']

for each in product(char, repeat=2):
    print(each)

for each in product(char, repeat=2):
    print(each)
# OutPut
('a', 'a', 'a')
('a', 'a', 'b')
('a', 'b', 'a')
('a', 'b', 'b')
('b', 'a', 'a')
('b', 'a', 'b')
('b', 'b', 'a')
('b', 'b', 'b')

permutation() 函数从输入的迭代的组合中生成指定长度的排列。它默认生成所有排列的完整集合。

from itertools import permutations

for each in permutations('abc'):
    print(each)
print()

for each in permutations('abc', r=2):
    print(each)

使用 r 参数来限制返回的单个排列的长度。

# OutPut
('a', 'b', 'c')
('a', 'c', 'b')
('b', 'a', 'c')
('b', 'c', 'a')
('c', 'a', 'b')
('c', 'b', 'a')

('a', 'b')
('a', 'c')
('b', 'a')
('b', 'c')
('c', 'a')
('c', 'b')

如果输出要保证唯一, 即需要组合而不是排列,请使用 combination() 。只要输入的成员是唯一的,输出就不会包含任何重复的值。

from itertools import combinations

for each in combinations('abc', r=2):
    print(each)

permutations() 不同的是, combination() 必须传入 r 参数。

# OutPut
('a', 'b')
('a', 'c')
('b', 'c')

因为 combination() 不重复单个输入元素,但考虑有时需要包含重复元素的组合。对于这些情况,可以使用 combinations_with_replacement()

from itertools import combinations_with_replacement

for each in combinations_with_replacement('abc', r=2):
    print(each)

在此输出中,每个输入项都与其自身以及输入序列的所有其他成员组合。

('a', 'a')
('a', 'b')
('a', 'c')
('b', 'b')
('b', 'c')
('c', 'c')

转载请注明来源 Python标准库笔记(10) — itertools模块

© 著作权归作者所有

共有 人打赏支持
j_hao104
粉丝 196
博文 66
码字总数 100580
作品 2
程序员
Python 函数式编程之迭代器、生成器及其应用

python 标准库中提供了 itertools, functools, operator 三个库支持函数式编程,对高阶函数的支持,python 提供 decorator 语法糖。 迭代器 (iterator)和生成器(generator)概念是 python 函数...

xrzs ⋅ 2015/09/24 ⋅ 0

程序员必备,快速学习 Python 的全套14张思维导图(附高清版下载)

后台回复关键词 思维导图 可获取本文中的高清思维导图(PDF版) ML & AI∣一个有用的公众号 长按,识别二维码,加关注 获取更多精彩文章

micf435p6d221ssdld2 ⋅ 05/23 ⋅ 0

0. Python3源码—编译

0.1. 整体架构 在最高的层次上,Python的整体架构可以分为三个主要的部分: 左:Python提供的大量的模块、库以及用户自定义的模块; 右:Python的运行时环境,包括对象/类型系统(Object/Ty...

whj0709 ⋅ 06/06 ⋅ 0

python学习笔记 | Python中并行IO操作的内存效率

Python允许多种不同的并行处理方法。并行性的主要问题是了解其局限性。我们要么平行IO操作或像图像处理这样的CPU限制任务。 在Python 3.5之前,有两种方法可以并行处理IO绑定操作。本地方法是...

跨界的聚能 ⋅ 05/24 ⋅ 0

Python官方库SSH Decorator被曝后门:可窃取SSH凭证

在代码库中隐藏后门的最后一次尝试已经过去了一个星期,今天我们又有了一个新的案例。这一次,后门是在一个Python模块中找到的,而不是一个npm (JavaScript)包。 该模块的名称是SSH Decorat...

云技术之家 ⋅ 05/15 ⋅ 0

156个Python网络爬虫资源,妈妈再也不用担心你找不到资源了

本列表包含Python网页抓取和数据处理相关的库。 前几天有私信小编要Python的学习资料,小编整理了一些有深度的Python教程和参考资料,从入门到高级的都有,文件已经打包好了,正在学习Pytho...

雁横 ⋅ 05/02 ⋅ 0

python资料全集

python: 微信公众号开发小记——2.80端口上的服务 python: 微信公众号开发小记——3.接入三方登录 使用python编写一个壁纸网站的简单爬虫 python: python List 用法 Python 中各个时间复杂度...

d_watson ⋅ 2016/04/15 ⋅ 0

python开源工具列表【持续更新】

以下是个人在工作中整理的一些python wheel,供参考。 这个列表包含与网页抓取和数据处理的Python库 网络 通用urllib -网络库(stdlib)。 requests -网络库。 grab – 网络库(基于pycurl)。...

武耀文 ⋅ 04/25 ⋅ 0

python高性能编程第一章读书笔记

计算机底层组件分为三大基本部分:计算单元、存储单元以及两者之间的连接。 计算单元:具有将接收到的任意输入转换成输出的能力以及改变当前处理状态的能力。CPU是最常见的计算单元。它的主要...

ma412410029 ⋅ 05/28 ⋅ 0

编程入门13:Python文本处理

上一篇:编程入门12:Python异常处理 计算机经常需要对文本进行各种操作,我们知道Python语言表示文本的类型是字符串(str),其中可以包含任何字符。系统内部以标准的“统一码”(Unicode)...

starglow_leo ⋅ 06/19 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Linux中的端口大全

1 被LANA定义的端口 端口 名称 描述 1 tcpmux TCP 端口服务多路复用 5 rje 远程作业入口 7 echo Echo 服务 9 discard 用于连接测试的空服务 11 systat 用于列举连接了的端口的系统状态 13 d...

寰宇01 ⋅ 15分钟前 ⋅ 0

Confluence 6 如何备份存储文件和页面信息

备份的 ZIP 文件包含有 entities.xml,这个 XML 文件包含有 Confluence 的所有页面内容和存储附件的目录。 备份 Zip 文件结构 页面的附件是存储在附件存储目录中的,通过页面和附件 ID 进行识...

honeymose ⋅ 18分钟前 ⋅ 0

【每天一个JQuery特效】根据状态确定是否滑入或滑出被选元素

主要效果: 本文主要采用slideToggle()方法实现以一行代码同时实现以展开或收缩的方式显示或隐藏被选元素。 主要代码如下: <!DOCTYPE html><html><head><meta charset="UTF-8">...

Rhymo-Wu ⋅ 22分钟前 ⋅ 0

度量.net framework 迁移到.net core的工作量

把现有的.net framework程序迁移到.net core上,是一个非常复杂的工作,特别是一些API在两个平台上还不能同时支持。两个类库的差异性,通过人工很难识别全。好在微软的工程师们考虑到了我们顾...

李朝强 ⋅ 27分钟前 ⋅ 0

请不要在“微服务”的狂热中迷失自我!

微服务在过去几年一直是一个非常热门的话题(附录1)。何为“微服务的疯狂”,举个例子: 众所周知,Netflix在DevOps上的表现非常棒。Netfix可以做微服务。因此:如果我做微服务,我也将非常...

harries ⋅ 28分钟前 ⋅ 0

oAuth2 升级Spring Cloud Finchley.RELEASE踩坑分享

背景 6.19号,spring团队发布了期待已久的 Spring Cloud Finchley.RELEASE 版本。 重要变化: 基于Spring Boot 2.0.X 不兼容 Spring Boot 1.5.X 期间踩过几个坑,分享出来给大伙,主要是关于...

冷冷gg ⋅ 58分钟前 ⋅ 0

OSChina 周一乱弹 —— 理发师小姐姐的魔法

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @冰冰棒- :分享田馥甄的单曲《My Love》 《My Love》- 田馥甄 手机党少年们想听歌,请使劲儿戳(这里) @Li-Wang :哎,头发又长了。。。又要...

小小编辑 ⋅ 今天 ⋅ 8

Kafka1.0.X_消费者API详解2

偏移量由消费者管理 kafka Consumer Api还提供了自己存储offset的功能,将offset和data做到原子性,可以让消费具有Exactly Once 的语义,比kafka默认的At-least Once更强大 消费者从指定分区...

特拉仔 ⋅ 今天 ⋅ 0

NEO智能合约之发布和升级(二)

接NEO智能合约之发布和升级(一),我们接下来说说智能合约的升级功能。 一 准备工作 合约的升级需要在合约内预先设置好升级接口,以方便在升级时调用。接下来我们对NEO智能合约之发布和升级...

红烧飞鱼 ⋅ 今天 ⋅ 0

个人博客的运营模式能否学习TMALL天猫质量为上?

心情随笔|个人博客的运营模式能否学习TMALL天猫质量为上? 中国的互联网已经发展了很多年了,记得在十年前,个人博客十分流行,大量的人都在写博客,而且质量还不错,很多高质量的文章都是在...

原创小博客 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部