`
frank-liu
  • 浏览: 1681347 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

python中的generator解读

 
阅读更多

简介

    在以前一篇讲述iterator的文章里,我提到过通过一种循环遍历的方式去访问一个python对象需要具备的特性。在那里,我们要实现一个iterator的时候,要定义一个可以访问的迭代器__iter__,同时也需要定义一个next()方法用来获取后面的一个元素。这是一种遍历集合元素的方式。这里主要依赖的是我们在一个集合元素已经取得的情况下。使用iterator的时候,我又碰到一个看起来很近似的东西,generator。generator有什么作用呢?为什么要专门折腾一个generator出来?我们一一讨论过来。

 

Generator和Iterator的比较

    我们来看一段很简单的代码:

def numlist(n):
...     while(n < 10):
...             yield n
...             n += 1

    这里有一个看似有点怪异的语法,yield n。如果我们要使用这个方法,该怎么用呢?

>>> for i in numlist(0):
...     print i,
... 
0 1 2 3 4 5 6 7 8 9

    从用法来看,这里好像是numlist()作为集合来遍历。如果我们了解iterator的话,我们完全也可以用iterator实现一个来达到同样的目的啊,比如说我们实现一个有同样输出的iterator如下:

class numlist:
    def __init__(self, start):
        self.count = start

    def __iter__(self):
        return self

    def next(self):
        if self.count >= 10:
            raise StopIteration
        r = self.count
        self.count += 1
        return r

    如果我们用如下的代码来遍历的话,也会得到如下的结果:

>>> for i in numlist(0):
...     print i,
... 
0 1 2 3 4 5 6 7 8 9

    从表面上看起来,他们两者都返回类似集合的东西,好像没什么区别。我们深入的分析一下看看。我们先来看看前面定义的generator部分:

>>> from genlist import numlist
>>> x = numlist(0)
>>> x
<generator object numlist at 0x7f217108a780>

    输出的结果显示numlist方法返回的结果是一个generator。

    而如果我们引用那个iterator的实现,则结果如下:

>>> from countup import numlist
>>> x = numlist(0)
>>> x
<countup.numlist instance at 0x7f17e42a1440>

     这里显示的x是一个numlist的实例对象。而如果我们进一步去深究的,这个x是什么详细的定义呢?我们在python命令行里输入help(x):

class numlist
 |  Methods defined here:
 |  
 |  __init__(self, start)
 |  
 |  __iter__(self)
 |  
 |  next(self)

    从结构上来说,这里就是我们前面讨论过的典型的iterator。我们再来看generator那部分的:

numlist = class generator(object)
 |  Methods defined here:
 |  
 |  __getattribute__(...)
 |      x.__getattribute__('name') <==> x.name
 |  
 |  __iter__(...)
 |      x.__iter__() <==> iter(x)
 |  
 |  __repr__(...)
 |      x.__repr__() <==> repr(x)
 |  
 |  close(...)
 |      close(arg) -> raise GeneratorExit inside generator.
 |  
 |  next(...)
 |      x.next() -> the next value, or raise StopIteration
 |  
 |  send(...)
 |      send(arg) -> send 'arg' into generator,
 |      return next yielded value or raise StopIteration.
 |  
 |  throw(...)
 |      throw(typ[,val[,tb]]) -> raise exception in generator,
 |      return next yielded value or raise StopIteration.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  gi_code
 |  
 |  gi_frame
 |  
 |  gi_running

    和前面的iterator不一样,这里有一个生成的generator类,然后在类里多了close, send等几个方法。看到这里的时候,我们发现generator里完全有和iterator一样的功能,比如通过next()方法来遍历他们。但是我们在前面定义的时候更加简练一些。

 

Generator和list的比较

     在前面这一部分我们比对了generator和iterator的结构形式。发现generator的功能似乎涵盖了iterator。除了我们前面实现代码的一种定义方式,我们还有一种实现generator的形式,它看起来和list comprehension很相似。我们看如下的代码:

>>> a = [1, 2, 3, 4]
>>> b = (2*x for x in a)
>>> b
<generator object <genexpr> at 0x16e5a00>
>>> for i in b: print i,
... 
2 4 6 8

    在前面的代码里,如果我们将b定义成[2*x for x in a],得到的结果则不一样了:

>>> c = [2*x for x in a]
>>> c
[2, 4, 6, 8]
>>> type(c)
<type 'list'>

    仅仅是一个括号的区别,返回的结果就完全不同。虽然我们也可以用generator来作为遍历的结果。那么generator和list遍历的结果有什么不一样呢?

    在前面的代码里,我们执行第一次循环generator的时候,输出一组数字,可是这个generator只能使用一次,而list却是可以使用无数次的:

>>> for i in b: print i,
... 
2 4 6 8
>>> for i in b: print i,
... 
>>> for i in a: print i,
... 
1 2 3 4
>>> for i in a: print i,
... 
1 2 3 4

    看来这个generator就像个一次性消费品,定义起来简单,但是一次就用完了。这么看来,generator还比较麻烦,还不如list呢。其实generator还有一个优点,就是我们一般遍历数据的时候,都是需要已经构造好一个集合了,然后再去遍历它们。这样如果在数据集合比较大的时候就变得不可行了。而generator并不预先构造一个集合。每次我们循环遍历的时候,循环一次就取一个数据。因为这么个特性,它不需要将所有的集合数据都保存下来。在一些大规模数据的处理的情况下,它显得更加高效率。

 

Generator的作用和思想

    在前面的描述中generator是一个可以无限取数据的流水线,对它的使用就像是操作一个集合一样,但是一旦启用了之后它从读取数据到所有数据操作完毕,它只能用一次。在一些场景中,我们可能需要对一些数据做多个步骤的处理。如果我们从一开始对数据进行操作的时候就使用generator的话,会发现后面都必须要利用它来做进一步的组合。我们来看一个示例。

    假设我们有一个log文件,那里记录了web服务器数据访问的信息,它里面保存的信息是如下格式的:

81.107.39.38 - ... "GET /ply/ply.html HTTP/1.1" 200 97238

    在一行最末尾的地方记录的是一次访问操作的时候传输的数据量大小,单位为字节。最后一位可以为一个数字或者为一个-字符。如果我们需要统计里面所有请求的数据量,那么该怎么来实现呢?下面是一种是我们想到的传统实现方法:

 

wwwlog = open("access-log")
total = 0
for line in wwwlog:
	bytestr = line.rsplit(None,1)[1]
	if bytestr != '-':
		total += int(bytestr)
print "Total", total

     这里的代码思路比较简单,首先我们打开这个log文件,然后读取文件的每一行,再将取到的没一行里的数据量相加,最后得到总数据传数量。

    如果我们结合前面generator的用法来看,这里还有一种写法:

wwwlog = open("access-log")
bytecolumn = (line.rsplit(None,1)[1] for line in wwwlog)
bytes = (int(x) for x in bytecolumn if x != '-')
print "Total", sum(bytes)

    我们取每一行里的数据量部分是返回一个generator,然后每次针对这个部分解析数值也是返回一个generator,最后再通过一个sum()方法来统计所有的和。在后面这种写法里,我们更多的像是在声明说要取哪些数据,然后每一步是在原来generator的基础上再套generator。有点流水线套流水线的味道。和前面的比起来,这种写法更加简单和直观。代码行数都更少一些。

 

总结

     generator的定义可以通过yield n或者类似于list comprehension的方法来构造。它本身不构造数据列表,因此可以处理理论上无穷的数据而不会导致机器的存储资源耗尽。generator类似于一个车间生产的流水线,每次需要用产品的时候才临时从那里取一个,然后这个流水线就停在那里等待下一次取操作。我们可以在实际应用中将多个generator串在一起来用,这一点和unix设计思想里pipeline的思想居然是惊人的一致。另外,如果我们使用generator写代码来表述一些处理的过程时,体现出来的更多是一种声明式的写法,这和函数式编程的思想居然有如此紧密的联系。关于这些关系值得以后文章里进一步的探讨。

 

参考材料

http://www.dabeaz.com/generators/

分享到:
评论

相关推荐

    14.5 iterator与generator|Pythonic与Python杂记|Python3.8入门 & 进阶 & 原生爬虫实战完全解读

    14.5_iterator与generator|Pythonic与Python杂记|Python3.8入门_&_进阶_&_原生爬

    py源码实例Python分块拆分txt文件中的数据

    ### 标题解读:py源码实例Python分块拆分txt文件中的数据 这个标题表明了文章将通过具体的Python代码示例来展示如何对txt文件中的数据进行分块拆分。分块拆分是指将一个大文件分成若干个小块进行处理的方法。这种...

    Python-在Tensorflow中实现GoogleBrain的BEGAN

    "Python-在Tensorflow中实现GoogleBrain的BEGAN" 这个标题揭示了我们将在TensorFlow框架下使用Python编程语言来实现一个特定的深度学习模型——BEGAN(Boundary Equilibrium Generative Adversarial Networks)。...

    python十天笔记1

    在"Python十天笔记"中,我们逐步深入理解了Python的核心概念和关键要素。以下是对笔记内容的详细解读: 第一天,我们首先配置了Python环境,并运行了Hello World程序,这是任何编程学习的起点。接着,通过`key_...

    Python人工智能课程 AI算法课程 Python机器学习与深度学习 15.GAN源代码讲解及GAN项目介绍 共24页.pdf

    这部分内容深入分析了一个典型的GAN模型的代码实现,包括生成器(Generator)和判别器(Discriminator)的设计与训练过程。重点讲解了Keras四步走: 1. **构建模型**:定义生成器和判别器的架构。 2. **编译模型**...

    Python-多种GAN算法的Chainer实现

    这暗示了内容将涉及Python编程技巧,以及机器学习领域的理论和实践,尤其是GANs在机器学习中的应用。 **压缩包子文件的文件名称列表:** 由于未提供具体的子文件名,我们只能推测可能包含的内容。一个名为"chainer-...

    python生成式的send()方法(详解)

    def generator(): while True: receive=yield 1 print('extra'+str(receive)) g=generator() print(next(g)) print(g.send(111)) print(next(g))  输出: 1 extra111 1 extraNone 1 为什么会这样呢,点进send就...

    详细解读tornado协程(coroutine)原理

    在Python中,协程通常是通过生成器(Generator)来实现的。生成器是一个包含`yield`表达式的函数,它能够生成一系列值,而不仅仅是返回一个单一的值。当执行到`yield`语句时,生成器会暂停并返回一个值,同时保留...

    Homograph-Generator

    6. **示例和文档**: 为了帮助用户理解和使用这个工具,它应该包含详细的使用指南和一些示例,解释如何运行工具以及如何解读输出结果。 使用"homograph-generator"这样的工具可以帮助安全研究人员测试和评估网站的同...

    Keras-GAN-master_keras_GaN_GPS_

    GANs是一种深度学习模型,由两部分组成:生成器(Generator)和判别器(Discriminator),它们在训练过程中相互博弈,以生成逼真的新数据。 首先,让我们详细了解一下Keras。Keras是一个高级神经网络API,它运行在...

    廖雪峰JavaScript Git 教程

    接着,教程深入JavaScript的高级特性,如闭包(closures)、面向对象编程中的类(class)和继承(inheritance)、迭代器(iterable)和生成器(generator)、以及作用域(scope)。掌握这些概念对于深入理解...

    Lab_TOF:蒙特卡洛·德尔·埃斯佩里门托

    在描述中提到的 "main_generator.py" 文件是整个实验的核心部分,它是用Python语言编写的。Python是数据科学、机器学习和数值计算等领域广泛使用的编程语言,因为它简洁明了且拥有丰富的库支持。在"main_generator....

    二维码的服务端生成 二维码

    - **编程语言**:如Python的`qrcode`库、Java的`javacode`库、JavaScript的`qrcode-generator`库等。 - **HTTP服务**:使用如Node.js、Python Flask或Django、Java Spring Boot等框架构建HTTP API,接收请求并返回...

    2020_TopologyGAN:拓扑

    **Python在拓扑GAN中的作用** Python是当前数据科学和机器学习领域的首选编程语言,因其丰富的库和易用性而受到广泛欢迎。在拓扑GAN项目中,Python扮演着核心角色。开发者可以利用Python的深度学习框架,如...

    GANs生成对抗网络代码文档等资源

    7. **论文解读**:GANs领域的最新进展通常体现在学术论文中。资源可能包含一些重要论文的摘要、解读或复现代码,帮助你跟踪领域前沿。 8. **可视化工具**:为了更好地理解和调试GANs,可能会有可视化工具或脚本,...

    TREX使用说明

    TREX(Traffic Generator Rex)是一种高性能的网络性能测试工具,主要应用于数据中心和云环境中的网络设备和应用的性能验证。它能够模拟大规模并发连接,生成复杂的流量模式,帮助测试网络设备在高负载下的稳定性和...

    DieHard_TRNG_Test

    标题 "DieHard_TRNG_Test" 指向的可能是一个测试随机数生成器(TRNG,True Random Number Generator)的项目,尤其是针对DieHard测试套件的实现。DieHard是一系列用于评估伪随机数生成器(PRNG)质量的统计测试,而...

    二维码生成

    这些说明通常会涵盖各种编程语言,如Python的`qrcode`库,JavaScript的`qrcode-generator`等,或者是用户界面友好的在线工具,如草料二维码、微信小程序等。 “当下软件园.url”可能是一个链接,指向一个网站,该...

    GAN-mnist.rar_GAN分类_GAN训练mnist_GAN识别_gan手写数字_gan识别mnist

    GAN是一种深度学习模型,由两部分组成:生成器(Generator)和判别器(Discriminator),它们通过对抗性训练来提升模型的生成能力。 **描述解读:** 描述提到,这个项目是用GAN网络进行手写数字识别,经过验证,其...

    linux 命令英文全称

    lex(LEXical analyser)是一个词法分析器(lexical analyzer generator)。 #### lisp=LIStProcessing LISP(LISt Processing)是一种函数式编程语言(functional programming language)。 #### ln=LiNk ln(LiNk)命令...

Global site tag (gtag.js) - Google Analytics