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. 时间轴QTimeLine

    一个动画由多张静态图片组成,每一张静态图片为一帧.每隔一定时间显示一帧,如果时间间隔非常短的话,那这些静态图片就会构成一个连续影像,动画由此而来.QTimeLine提供了用于控制动画的时间轴,它在实现 ...

  2. JavaScript之浏览器兼容问题与IE(神经病一样的浏览器)

    IE是最讨厌的浏览器,没有之一.----题记 废话不说,粘上大图~

  3. 2017CCPC秦皇岛 C题Crusaders Quest&&ZOJ3983【模拟+STL】

    链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3983 题意: 给定9个血槽,有三种物品,每次可以把连续相同的物品抵消 ...

  4. Biorhythms HDU - 1370 (中国剩余定理)

    孙子定理: 当前存在三个式子,t%3=2,t%5=3,t%7=2.然后让你求出t的值的一个通解. 具体过程:选取3和5的一个公倍数t1能够使得这个公倍数t1%7==1,然后选取3和7的一个公倍数t2使 ...

  5. mysql 原理 ~ sql执行

    一 普通sql执行的具体过程1 连接器  管理连接,权限验证2 分析器  词法分析,语法分析   比如 数据表和数据列是否存在, 别名是否有歧义,是否符合标准sql语法等3 优化器检测   执行计划生 ...

  6. escape、encodeURI和encodeURIComponent的区别

    1.简单解释 简单来说,escape是对字符串(string)进行编码(而另外两种是对URL),作用是让它们在所有电脑上可读. 编码之后的效果是%XX或者%uXXXX这种形式. 其中 ASCII字母. ...

  7. How to Repair GRUB2 When Ubuntu Won’t Boot

    Ubuntu and many other Linux distributions use the GRUB2 boot loader. If GRUB2 breaks—for example, if ...

  8. python计算最大公约数和最小公倍数

    a=4 b=2 def gcd(a,b): return a if b==0 else gcd(b,a%b) def lcm(a,b): return a*b//gcd(a,b) print(gcd( ...

  9. kafka系列六、java管理kafka Topic

    package com.example.demo.topic; import kafka.admin.AdminUtils; import kafka.admin.RackAwareMode; imp ...

  10. 默认以管理员身份运行VS2013/15/17

    方法如下: 1.右击VS的快捷方式,选择[属性],打开属性对话框,再点击[高级]按钮,如下图所示: 2.再勾选[用管理员身份运行],点击[确定]即可: 然后就可以双击VS快捷方式,直接以管理员身份运行 ...