1、想象一个场景:

      设想,我想要100个素数,然后对它们累加求和。

通常的想法是,找一个一次性至少能提供100个素数的工具(函数),让它把这100个素数交给我(用return 一次性返回含100个素数的列表)。对于100个素数,这显然是可行的。但是,如果我想要无穷多个素数累加,或者非常非常多素数累加,多到几乎可以消耗掉整个内存空间,这时候找一个工具一次性交付这么多数据就显然非常不合适了。

对于之前的设想,我们还可以有另一种想法,就是找一个能不断生产素数的工具,这个工具并不一次交付100个素数,仅在我跟它索要数据的时候它才给出下一个素数(记住,这个工具是有记忆的,它知道我之前取了哪些数,并给出我现在需要的素数。换句话说,它交付给我一个素数后,它的状态被保留了,它仅仅是被挂起而没有被销毁),有点类似母鸡下蛋,拍一拍便下一个蛋,然后就待产了。试想,如果有这个工具,即使让它提供无穷多素数也完全可行,因为它每次只提供一个素数,完全不存在内存耗尽的问题。那么,这个工具存在吗?当然存在,而且名字就叫生成器(generator)。

2、生成器(generator)原理简介:

在python中有一个关键字yield(yield有生产的意思)。当你在函数中使用了yield,你的函数就发生变化了,它变成了生成器(generator)。当你调用这个函数的时候,它不返回值,而是返回一个生成器对象。一个生成器就这样产生了,一个函数加上一个关键字。虽然简单,但是我们还是迷惑,因为我们还不清楚generator的工作流程。那下面就通过一些简单的例子来跟踪一下它吧。

例1:

我们定义了一个函数(def test()),并使用了关键字yield(yield是生产的意思,其后的'hello'就是它即将生产的值了),于是一个generator就产生了(调用test(),返回了一个generator object test)。我们把这个generator赋给变量h。现在我们用next()启动生成器(注:python2.x可以写作h.next()),产生了一个值'hello'。

例2:接例1,如果我们继续使用next(),再次启动generator会怎样呢?

结果发生了StopIterator(终止迭代器)异常。为什么会这样呢,为什么没有产生'hello',难道再次启动generator时没有从函数(test())的第一行开始执行吗?显然是的,不然就真的输出'hello'了。那第二次启动generator时,函数的入口点在哪里?别急,我们先看下面的例子。

例3:

我在原来test()的基础上又增加了一个yield,第一次启动generator输出'hello',第二次启动generator输出'world'。为什么会这样呢,为什么第一次启动generator没有输出'world'?这就要涉及到generator的工作原理了。

当我们使用next()第一次启动generator的时候,函数的入口点在第一行,也就是说,函数从test()的第一行开始执行。当执行到关键字yield处的时候,返回'hello',同时把控制权交给调用方,自己则把当前状态保留(包括变量值,函数执行状态等),准备被再次调用(还记得‘记忆’吗,是的,generator保留了记忆,而普通函数调用完后所有的数据都要销毁,没有记忆存留)。当第二次启动generator时,函数的入口点变成了>>yield 'world',执行完该语句后,generator继续移交控制权,保存当前状态挂起。

回到例2的问题,为什么会产生StopIterator?根据generator的执行原理,第二次启动generator时,函数入口点在yield 'hello'下一行,而它的一下一行什么都没有,自然会返回异常了。那是不是说我在yield 'hello' 后任加一条语句就不会异常,而且可以执行该语句了呢?在例3中我们加了一条yield语句,结果是很满意的。现在我们再加一条打印语句看看情况吧。

例4:

异常又发生了!第二次启动generator时,print 语句没有执行。为什么会这样呢?原来,generator每次启动执行都要寻找关键字yield,当它找不到下一个yield的时候就会说:“StopIterator”。这也就是为什么例3可以顺利执行,例4却会发生异常。

例5:通过前面的例子,你也许已经大概了解了generator的工作原理,要熟练它还得多练习才行。但是,到这里还只是认识了generator的基础而已。在前几个例子中,yield是作为语句出现的,事实上,它还可以是运算式,这个时候我们需要借助h.send(msg)。先看例子,再详细解释吧。

我在函数中加了一个赋值运算符('=')。第二次启动generator时,我使用了send(msg)来发送消息,msg是消息内容,send()可以像next()一样启动generator,同时它还会把作为其参数的msg发送给yield后面的变量a。记住,如果不给a通过send(msg)赋值,a=None。你是不是还想知道,假设有多个像a一样的变量(var = yield ),send(msg)发送的消息会传给谁呢。我们看下面的例子。

例6:

显然,send('he')把消息送给了b,因为b距离下一个yield最近吗?你可以自己试试。你有没有发现,似乎我们写了这么多函数,但是都没有见到return。在generator中return如何自处呢,或者说return和我们的yield是什么关系呢?

在generator中yield相当于普通函数的return,但是又不完全相同。相似点是,函数在执行过程中碰到return/yield时都会移交控制权,不同点是,return移交后会销毁被调函数的一切状态,yield则将函数暂时挂起。

----------------------------------------------------------------------------------

参考资料:

1、提高你的python:解释'yield'和'generators':http://www.oschina.net/translate/improve-your-python-yield-and-generators-explained

2、python Gossip:yield 产生器:http://openhome.cc/Gossip/Python/YieldGenerator.html

3、小记python yield 与生成器:http://blog.bitfoc.us/?p=502

python--yield and generator(生成器)简述的更多相关文章

  1. Python高级语法之:一篇文章了解yield与Generator生成器

    Python高级语法中,由一个yield关键词生成的generator生成器,是精髓中的精髓.它虽然比装饰器.魔法方法更难懂,但是它强大到我们难以想象的地步:小到简单的for loop循环,大到代替多 ...

  2. generator生成器iterator遍历器和yield

    generator方法()返回一个iterator 使用generator时永远先去调用generator()方法 for of对iterator的调用过程(babel参照) 1,_iterator. ...

  3. 【Python】迭代器、生成器、yield单线程异步并发实现详解

    转自http://blog.itpub.net/29018063/viewspace-2079767 大家在学习python开发时可能经常对迭代器.生成器.yield关键字用法有所疑惑,在这篇文章将从 ...

  4. Python入门之迭代器/生成器/yield的表达方式/面向过程编程

    本章内容 迭代器 面向过程编程 一.什么是迭代 二.什么是迭代器 三.迭代器演示和举例 四.生成器yield基础 五.生成器yield的表达式形式 六.面向过程编程 ================= ...

  5. Python高级编程之生成器(Generator)与coroutine(二):coroutine介绍

    原创作品,转载请注明出处:点我 上一篇文章Python高级编程之生成器(Generator)与coroutine(一):Generator中,我们介绍了什么是Generator,以及写了几个使用Gen ...

  6. Python高级编程之生成器(Generator)与coroutine(一):Generator

    转载请注明出处:点我 这是一系列的文章,会从基础开始一步步的介绍Python中的Generator以及coroutine(协程)(主要是介绍coroutine),并且详细的讲述了Python中coro ...

  7. Python的程序结构[7] -> 生成器/Generator -> 生成器浅析

    生成器 / Generator 目录 关于生成器 生成器与迭代器 生成器的建立 通过迭代生成器获取值 生成器的 close 方法 生成器的 send 方法 生成器的 throw 方法 空生成器的检测方 ...

  8. python yield generator 详解

    本文将由浅入深详细介绍yield以及generator,包括以下内容:什么generator,生成generator的方法,generator的特点,generator基础及高级应用场景,genera ...

  9. python关于type()与生成器generator的用法

    如果按这种形式写  type(a)(b) 那此处的b是个可迭代对象,这个对象迭代完成后,再放到type里    from pymysql._compat import range_type, text ...

  10. Python yield与实现

    Python yield与实现  yield的功能类似于return,但是不同之处在于它返回的是生成器. 生成器 生成器是通过一个或多个yield表达式构成的函数,每一个生成器都是一个迭代器(但是迭 ...

随机推荐

  1. JavaJavaScript之内存与变量初始化

    0.搞清三个概念:预加载与执行期:js变量存储(栈区与堆区):js变量的类型(引用类型(对象)与基本数据类型); JS在预编译时,对于函数的预加载方面,浏览器仅仅选择编译声明式函数(function ...

  2. JavaScript之数值计算

    //两等长数组对应元素之间做减法运算[可拓展:基本运算(+/-*//)] function array_dif(length,arrayA,arrayB){ var array = new Array ...

  3. 利用 python requests完成接口文件上传

    最近在准备一个公开课,主题就是利用不同的语言和不同的工具去实现文件的上传和下载. 在利用Jmeter去实现功能的时候,以及利用loadrunner去写脚本的时候,都很顺利,没有任何问题,当我尝试用Py ...

  4. python - class类 (一)

    三大编程范式 1.面向过程 2.函数式 3.面向对象 注意 编程范式没有高低之分,只有适用不适用. 面向对象编程: 编程是程序源用特定的语法+数据结构+算法组成的代码来告诉计算机如何执行任务的过程,一 ...

  5. Docker帮助命令

    ①docker version ②docker info ③docker --help

  6. DDR3基本知识及测试【转】

    转自:http://blog.csdn.net/myarrow/article/details/7847385 一.DDR3简介 DDR3(double-data-rate three synchro ...

  7. 分布式系列 - dubbo服务telnet命令【转】

    dubbo服务发布之后,我们可以利用telnet命令进行调试.管理.Dubbo2.0.5以上版本服务提供端口支持telnet命令,下面我以通过实例抛砖引玉一下: 1.连接服务 测试对应IP和端口下的d ...

  8. kafka系列三、Kafka三款监控工具比较

    转载原文:http://top.jobbole.com/31084/ 通过研究,发现主流的三种kafka监控程序分别为: Kafka Web Conslole Kafka Manager KafkaO ...

  9. python系统编码转换

    # coding:gbk import sys import locale def p(f): print '%s.%s(): %s' % (f.__module__, f.__name__, f() ...

  10. Ex 5_32 一台服务器当前有n个等待服务的顾客...第八次作业

    设第i个客户需要等待的时间为ti,则n个客户需要总的等待时间为 ,因此,要使T最小,则要使 即可,所以,对所有的ti按升序进行排序和服务将得到最小的等待时间. package org.xiu68.ch ...