在Python中使用itertools模块中的组合函数的教程

理解新概念

Python V2.2 中引入了迭代器的思想。唔,这并不十分正确;这种思想的“苗头”早已出现在较老的函数 xrange() 以及文件方法 .xreadlines() 中了。通过引入 yield 关键字,Python 2.2 在内部实现的许多方面推广了这一概念,并使编程定制迭代器变得更为简单( yield 的出现使函数转换成生成器,而生成器反过来又返回迭代器)。

迭代器背后的动机有两方面。将数据作为序列处理通常是最简单的方法,而以线性顺序处理的序列通常并不需要都同时实际 存在。

x*() 前兆提供了这些原理的清晰示例。如果您想对某操作执行成千上万次,那么执行您的程序可能要花些时间,但该程序一般不需要占用大量内存。同样,对于许多类型的文件,可以一行一行地处理,且不需要将整个文件存储在内存中。最好对其它所有种类的序列也进行惰性处理;它们可能依赖于通过通道逐步到达的数据,或者依赖于一步一步执行的计算。

大多数时候,迭代器用在 for 循环内,就象真正的序列那样。迭代器提供了 .next() 方法,它可以被显式调用,但有百分之九十九的可能,您所看到的是以下行:

for x in iterator:
  do_something_with(x)

在对 iterator.next() 进行幕后调用而产生 StopIteration 异常时,该循环就被终止。顺便说一下,通过调用 iter(seq) ,实际序列可以被转换成迭代器 - 这不会节省任何内存,但是在下面讨论的函数中它会很有用。

Python 不断发展的分裂性格

Python 对函数编程(FP)的观点有点相互矛盾。一方面,许多 Python 开发人员轻视传统的 FP 函数 map() 、 filter() 和 reduce() ,常常建议使用“列表理解”来代替它们。但完整的 itertools 模块恰恰是由与这些函数类型完全相同的函数组成的,只不过这些函数对“惰性序列”(迭代器)操作,而不是对完整的序列(列表,元组)操作。而且,Python 2.3 中没有任何“迭代器理解”的语法,这似乎与列表理解拥有一样的动机。

我猜想 Python 最终会产生某种形式的迭代器理解,但这取决于找到合适于它们的自然语法。同时,在 itertools 模块中,我们拥有大量有用的组合函数。大致地,这些函数中的每一个都接受一些参数(通常包含一些基础迭代器)并返回一个新迭代器。例如,函数 ifilter() 、 imap() 和 izip() 都分别直接等同于缺少词首 i 的内置函数。

缺少的等价函数

itertools 中没有 ireduce() ,尽管按道理很自然地应该有这个函数;可能的 Python 实现是:
清单 1. ireduce() 的样本实现

def ireduce(func, iterable, init=None):
  if init is None:
    iterable = iter(iterable)
    curr = iterable.next()
  else:
    curr = init
  for x in iterable:
    curr = func(curr, x)
    yield curr

ireduce() 的用例类似于 reduce() 的用例。例如,假设您想要添加某个大型文件所包含的一列数字,但是当满足一个条件时就停止。您可以使用以下代码来监控正在计算的合计数:
清单 2. 添加并合计一列数

from operator import add
from itertools import *
nums = open('numbers')
for tot in takewhile(condition, ireduce(add, imap(int, nums)):
  print "total =", tot

一个更实际的示例可能类似于将事件流应用于有状态对象上,例如应用到 GUI 窗口小部件上。但是即使是上述简单示例也显示了迭代器组合器的 FP 风格。

基本迭代器工厂

itertools 中的所有函数都可以用纯 Python 轻松地实现为生成器。在 Python 2.3+ 中包含该模块的要点是为一些有用的函数提供标准行为和名称。尽管程序员可以编写他们自己的版本,但是每个人实际创建的变体都会有点不兼容。但是,另一方面是要以高效率的 C 代码实现迭代器组合器。使用 itertools 函数将比编写您自己的组合器稍微快一些。标准文档显示了每个 itertools 函数的等价纯 Python 实现,所以不需要在本文中重复这些内容了。

itertools 中的函数再基本不过了 - 而且命名也完全不同 - 这样从该模块导入所有名称可能就有意义了。例如,函数 enumerate() 可能明显地出现在 itertools 中,但是它在 Python 2.3+ 中却是一个内置函数。值得注意的是,您可以用 itertools 函数很方便地表达 enumerate() :

from itertools import *
enumerate = lambda iterable: izip(count(), iterable)

让我们首先看一下几个 itertools 函数,它们 没有将其它迭代器作为基础,而完全是“从头”创建迭代器。 times() 返回一个多次产生同一对象的迭代器;在本质上,这一能力比较有用,但它确实可以很好地替代使用过多的 xrange() 和索引变量,从而可以简单地重复一个操作。即,不必使用:

for i in xrange(1000):
  do_something()

您现在就可以使用更中性的:

for _ in times(1000):
  do_something()

如果 times() 只有一个参数,那么它只会重复产生 None 。函数 repeat() 类似于 times() ,但它无界地返回同一对象。不管是在包含独立 break 条件的循环中还是在象 izip() 和 imap() 这样的组合器中,这个迭代器都很有用。

函数 count() 有点类似于 repeat() 和 xrange() 的交叉。 count() 无界地返回连续整数(以可选的参数为开始)。但是,如果 count() 当前不支持溢出到现在正确的 longs,那么您可能还是要使用 xrange(n,sys.maxint) ;它并不是完全无界的,但是对于大多数用途,它实际上是一回事。类似于 repeat() , count() 在其它迭代器组合器内部特别有用。

组合函数

我们已经顺便提到了 itertools 中的几个实际组合函数。 ifilter() 、 izip() 和 imap() 的作用就象您会期望从它们相应的序列函数上获得的作用。 ifilterfalse() 很方便,所以您不需要去掉 lambda 和 def 中的谓词函数(而且这还节省了大量的函数调用开销)。但是在功能上,您可以将 ifilterfalse() 定义为(大致的情况,忽略了 None 谓词):

def ifilterfalse(predicate, iterable):
  return ifilter(lambda predicate: not predicate, iterable)

函数 dropwhile() 和函数 takewhile() 根据谓词对迭代器进行划分。 dropwhile() 在直到满足某个谓词 之前忽略所产生的元素, takewhile() 在满足某个谓词 时就终止。 dropwhile() 跳过迭代器的不定数目的初始元素,所以它可能直到某个延迟后才开始迭代。 takewhile() 马上开始迭代,但是如果被传入的谓词变为真,那么就终止迭代器。

函数 islice() 基本上就是列表分片的迭代器版本。您可以指定开始、停止和步长,就象使用常规的片。如果给定了开始,那么会删除大量元素,直到被传递的迭代器到达满足条件的元素为止。这是另一个我认为可以对 Python 进行改进的情形 - 迭代器最好只识别片,就象列表所做的(作为 islice() 行为的同义词)。

最后一个函数 starmap() 在 imap() 基础上略微有些变化。如果这个作为参数传递的函数获取多个参数,那么被传递的 iterable 会产生大小适合的元组。这基本上与包含多个被传入 iterable 的 imap() 相同,只不过它包含先前与 izip() 结合在一起的 iterables 集合。

深入探讨

itertools 中包含的函数是一个很好的开始。不使用其它函数,只用这些函数就可以让 Python 程序员更轻松地利用和组合迭代器。一般说来,迭代器的广泛使用对 Python 的未来无疑是很重要的。但是除了过去所包含的内容以外,我还要对该模块的将来更新提几点建议。您可以立即很方便地使用这些函数 - 当然,如果它们是后来被包含进来的,那么名称或接口细节会有所不同。

一种可能会很通用的类别是一些将多个 iterable 作为参数,随后从每个 iterable 产生单独元素的函数。与此相对照的是, izip() 产生元素元组,而 imap() 产生从基本元素计算而来的值。我头脑中很清晰的两个安排是 chain() 和 weave() 。第一个在效果上类似于序列并置(但是有点惰性)。即,在您可能使用的纯序列中,例如:

for x in ('a','b','c') + (1, 2, 3):
  do_something(x)

对于一般的 iterables,您可以使用:

for x in chain(iter1, iter2, iter3):
  do_something(x)

Python 实现是:
清单 3. chain() 的样本实现

def chain(*iterables):
  for iterable in iterables:
    for item in iterable:
      yield item

使用 iterables,您还可以通过使它们分散排列来组合几个序列。还没有任何对序列执行这样相同操作的内置语法,但是 weave() 本身也非常适用于完整的序列。下面是可能的实现(Magnus Lie Hetland 提出了 comp.lang.python 的类似函数):
清单 4. weave() 的样本实现

def weave(*iterables):
  "Intersperse several iterables, until all are exhausted"
  iterables = map(iter, iterables)
  while iterables:
    for i, it in enumerate(iterables):
      try:
        yield it.next()
      except StopIteration:
        del iterables[i]

让我来演示一下 weave() 的行为,因为从实现上看不是很明显:

>>> for x in weave('abc', xrange(4), [10,11,12,13,14]):
...  print x,
...
a 0 10 b 1 11 c 2 12 13 3 14

即使一些迭代器到达终点,但其余迭代器会继续产生值,直到在某一时刻产生了所有可用的值为止。

我将另外只提出一个可行的 itertools 函数。提出这个函数主要是受到了构思问题的函数编程方法的启发。 icompose() 与上面提出的函数 ireduce() 存在某种对称。但是在 ireduce() 将值的(惰性)序列传递给某个函数并产生每个结果的地方, icompose() 将函数序列应用于每个前趋函数的返回值。可以把 ireduce() 用于将事件序列传递给长期活动的对象。而 icompose() 可能将对象重复地传递给返回新对象的赋值函数。第一种方法是相当传统的考虑事件的 OOP 方法,而第二种的思路更接近于 FP。

以下是可能的 icompose() 实现:
清单 5. icompose() 的样本实现

def icompose(functions, initval):
  currval = initval
  for f in functions:
    currval = f(currval)
    yield currval

结束语

迭代器 - 被认为是惰性序列 - 是功能强大的概念,它开启了 Python 编程的新样式。但是在只把迭代器当作数据源与把它作为一种序列来考虑之间存在着微妙的差别。这两种想法本质上哪一种都不见得比另一种更正确,但是后者开创了操作编程事件的一种组合性的简略表达方法。 itertools 中的组合函数(尤其是它可能产生的一些类似于我建议的函数)接近于编程的声明样式。对我而言,这些声明样式一般都更不易出错且更强大。

(0)

相关推荐

  • Python itertools模块详解

    这货很强大, 必须掌握 文档 链接 http://docs.python.org/2/library/itertools.html pymotw 链接 http://pymotw.com/2/itertools/ 基本是基于文档的翻译和补充,相当于翻译了 itertools用于高效循环的迭代函数集合 组成 总体,整体了解 无限迭代器 复制代码 代码如下: 迭代器         参数         结果                                              

  • 介绍Python中内置的itertools模块

    Python的内建模块itertools提供了非常有用的用于操作迭代对象的函数. 首先,我们看看itertools提供的几个"无限"迭代器: >>> import itertools >>> natuals = itertools.count(1) >>> for n in natuals: ... print n ... 1 2 3 ... 因为count()会创建一个无限的迭代器,所以上述代码会打印出自然数序列,根本停不下来,只

  • 详解Python的迭代器、生成器以及相关的itertools包

    对数学家来说,Python这门语言有着很多吸引他们的地方.举几个例子:对于tuple.lists以及sets等容器的支持,使用与传统数学类似的符号标记方式,还有列表推导式这样与数学中集合推导式和集的结构式(set-builder notation)很相似的语法结构. 另外一些很吸引数学爱好者的特性是Python中的iterator(迭代器).generator(生成器)以及相关的itertools包.这些工具帮助人们能够很轻松的写出处理诸如无穷序列(infinite sequence).随机过程

  • python 排列组合之itertools

    python 2.6 引入了itertools模块,使得排列组合的实现非常简单: 复制代码 代码如下: import itertools 有序排列:e.g., 4个数内选2个排列: 复制代码 代码如下: >>> print list(itertools.permutations([1,2,3,4],2))[(1, 2), (1, 3), (1, 4), (2, 1), (2, 3), (2, 4), (3, 1), (3, 2), (3, 4), (4, 1), (4, 2), (4,

  • Python中itertools模块用法详解

    本文实例讲述了Python中itertools模块用法,分享给大家供大家参考.具体分析如下: 一般来说,itertools模块包含创建有效迭代器的函数,可以用各种方式对数据进行循环操作,此模块中的所有函数返回的迭代器都可以与for循环语句以及其他包含迭代器(如生成器和生成器表达式)的函数联合使用. chain(iter1, iter2, ..., iterN): 给出一组迭代器(iter1, iter2, ..., iterN),此函数创建一个新迭代器来将所有的迭代器链接起来,返回的迭代器从it

  • Python标准库之循环器(itertools)介绍

    在循环对象和函数对象中,我们了解了循环器(iterator)的功能.循环器是对象的容器,包含有多个对象.通过调用循环器的next()方法 (__next__()方法,在Python 3.x中),循环器将依次返回一个对象.直到所有的对象遍历穷尽,循环器将举出StopIteration错误. 在for i in iterator结构中,循环器每次返回的对象将赋予给i,直到循环结束.使用iter()内置函数,我们可以将诸如表.字典等容器变为循环器.比如: 复制代码 代码如下: for i in ite

  • Python标准库之itertools库的使用方法

    前言 因为最近事情不是很多,想写一些技术文章分享给大家,同时也对自己一段时间来碎片化接受的知识进行一下梳理,所谓写清楚才能说清楚,说清楚才能想清楚,就是这个道理了. 很多人都致力于把Python代码写得更Pythonic,一来更符合规范且容易阅读,二来一般Pythonic的代码在执行上也更有效率.今天就先给大家介绍一下Python的系统库itertools.下面话不多说了,来一起看看详细的介绍吧. itertools库 迭代器(生成器)在Python中是一种很常用也很好用的数据结构,比起列表(l

  • Python 过滤字符串的技巧,map与itertools.imap

    具体的实例 我们需要在目录中遍历,包括子目录(哈哈),找出所有后缀为:rmvb ,avi ,pmp 的文件.(天哪?!你要干什么?这可是我的隐私啊--) 复制代码 代码如下: import os def anyTrue(predicate, sequence): return True in map(predicate, sequence) def filterFiles(folder, exts): for fileName in os.listdir(folder): if os.path.

  • 在Python中使用itertools模块中的组合函数的教程

    理解新概念 Python V2.2 中引入了迭代器的思想.唔,这并不十分正确:这种思想的"苗头"早已出现在较老的函数 xrange() 以及文件方法 .xreadlines() 中了.通过引入 yield 关键字,Python 2.2 在内部实现的许多方面推广了这一概念,并使编程定制迭代器变得更为简单( yield 的出现使函数转换成生成器,而生成器反过来又返回迭代器). 迭代器背后的动机有两方面.将数据作为序列处理通常是最简单的方法,而以线性顺序处理的序列通常并不需要都同时实际 存在

  • python的scipy.stats模块中正态分布常用函数总结

    python的scipy.stats模块是连续型随机变量的公共方法,可以产生随机数,通常是以正态分布作为scipy.stats的基本使用方法.本文介绍正态分布的两种常用函数:1.累积概率密度函数stats.norm.cdf(α,均值,方差):2.概率密度函数stats.norm.pdf(α,均值,方差). 1.stats.norm.cdf(α,均值,方差):累积概率密度函数 使用格式 status.norm.cdf(Norm) # 相当于已知正态分布函数曲线和x值,求函数x点左侧积分 使用实例

  • Python使用itertools模块实现排列组合功能示例

    本文实例讲述了Python使用itertools模块实现排列组合功能.分享给大家供大家参考,具体如下: 一.笛卡尔积:itertools.product(*iterables[, repeat]) 直接对自身进行笛卡尔积: import itertools for i in itertools.product('ABCD', repeat = 2): print (''.join(i),end=' ') 输出结果: AA AB AC AD BA BB BC BD CA CB CC CD DA D

  • Python中的测试模块unittest和doctest的使用教程

    我要坦白一点.尽管我是一个应用相当广泛的公共域 Python 库的创造者,但在我的模块中引入的单元测试是非常不系统的.实际上,那些测试大部分 是包括在 gnosis.xml.pickle 的 Gnosis Utilities 中的,并由该子软件包(subpackage)的贡献者所编写.我还发现,我下载的绝大多数第三方 Python 包都缺少完备的单元测试集. 不仅如此,Gnosis Utilities 中现有的测试也受困于另一个缺陷:您经常需要在极其大量的细节中去推定期望的输出,以确定测试的成败

  • python中itertools模块使用小结

    Python 内置的 itertools 模块包含了一系列用来产生不同类型迭代器的函数或类,这些函数的返回都是一个迭代器,我们可以通过 for 循环来遍历取值,也可以使用 next() 来取值. itertools 模块提供的迭代器函数有以下几种类型: 无限迭代器:生成一个无限序列,比如自然数序列 1, 2, 3, 4, ...: 有限迭代器:接收一个或多个序列(sequence)作为参数,进行组合.分组和过滤等: 组合生成器:序列的排列.组合,求序列的笛卡儿积等. itertools 高效循环

  • pytorch中的transforms模块实例详解

    pytorch中的transforms模块中包含了很多种对图像数据进行变换的函数,这些都是在我们进行图像数据读入步骤中必不可少的,下面我们讲解几种最常用的函数,详细的内容还请参考pytorch官方文档(放在文末). data_transforms = transforms.Compose([ transforms.RandomResizedCrop(224), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms

  • Python标准库itertools的使用方法

    Python标准库itertools模块介绍 itertools是python内置的模块,使用简单且功能强大,这里尝试汇总整理下,并提供简单应用示例:如果还不能满足你的要求,欢迎加入补充. 使用Python标准库itertools只需简单一句导入:import itertools chain() 与其名称意义一样,给它一个列表如 lists/tuples/iterables,链接在一起:返回iterables对象. letters = ['a', 'b', 'c', 'd', 'e', 'f']

  • python中itertools模块zip_longest函数详解

    最近在看流畅的python,在看第14章节的itertools模块,对其itertools中的相关函数实现的逻辑的实现 其中在zip_longest(it_obj1, ..., it_objN, fillvalue=None)时,其函数实现的功能和内置zip函数大致相同(实现一一对应), 不过内置的zip函数是已元素最少对象为基准,而zip_longest函数是已元素最多对象为基准,使用fillvalue的值来填充 以下是自己总结此函数的大致实现方法,和官方方法不同: 思路大致如此: 找出元素个

随机推荐