首先说明一下生成器也是迭代器,也有迭代器的那些优点。

那为什么要生成器呢?因为到目前为止都 不是你写的迭代器,都是别人定义好的。那如何自己去造一个迭代器呢?下面的内容就会给你答案。

想要自己造一个迭代器,我们可以根据迭代器的特征(只要一个对象有__iter____next__方法那它就是迭代器),自己定义一个类,然后定义一个__iter__()__next__(), 然后这个类实例化的对象就是一个迭代器啦。

但是这样写太麻烦啦!何况我们现在还没有学到类的知识,怎么办?给你一个魔法棒,让你快速优雅高效地造一个迭代器。

yield关键字

第一种自造迭代器的方法就是使用yield关键字。具体怎么实现呢?
非常简单,如下所示:

  1. def g():
  2. print("Hey~ 生成器")
  3. yield 1

上面的写法非常类似于函数的定义,相当于把return换成了yield(当然,并没有这么简单)。

此时,我执行g()返回的就是一个生成器。就是这么简单。

  1. ret = g()
  2. print(ret) #输出<generator object g at 0x101fef6d0>

但是这里有个特别需要注意的地方,也是与函数最明显的区别:

我们执行g()的时候,并没有打印"Hey~ 生成器",就像函数没执行一样。
这也是生成器一个非常重要的特点,那就是你执行g()返回的是一个生成器,同时只有在迭代它(调用它的__next__())的时候它才开始执行内部代码,碰到yield关键字就返回yield后面的值并停止。

  1. print(ret.__next__()) $print(next(ret))

输出:

  1. Hey~ 生成器
  2. 1

当然for循环它也是可以的:

  1. for i in ret:
  2. print(i)

输出:

  1. Hey~ 生成器
  2. 1

yield还可以多次执行,这与return也有区别。

  1. def g2():
  2. print("Hey~ 生成器1")
  3. yield 1
  4. print("Hey~ 生成器2")
  5. yield 2
  6.  
  7. ret = g2()

此时,你执行下next(ret),会打印"嘿!生成器1",然后返回一个1,再执行一次next(ret),会打印出"嘿!生成器2",然后返回一个2

  1. print(next(ret))
  2. # 输出
  3. Hey~ 生成器1
  4. 1
  5. print(next(ret))
  6. # 输出
  7. Hey~ 生成器2
  8. 2

yield与return的区别

在一个函数里return只能执行一次,return之后函数就彻底结束了:

  1. def test_return():
  2. return 1
  3. return 2 #永不执行
  4. return 3 #永不执行

yield之后可以保存函数的运行状态,下次继续执行:

  1. def test_yield():
  2. yield 1
  3. yield 2 #下次next()后执行
  4. yeild 3 #下次next()后执行

下面的例子中,使用return时,只能返回0

  1. def test_return2():
  2. for i in range(10):
  3. return i #只能返回0,函数就结束了

使用yield能够依次返回0~9

  1. def test_yield2():
  2. for i in range(10):
  3. yield i #每调用一次next()就会一次弹出0~9

yield的作用

  1. yield把函数变成了生成器(生成器就是迭代器)。
  2. 为函数封装好了__iter____next__方法,把函数的执行结果做成了迭代器。
  3. 遵循迭代器的取值方式 — obj.__next__(),触发的是函数的执行。函数暂停与继续执行的状态都是由yield保存的。

倒计时的例子:

  1. def countdown(n):
  2. print("倒计时开始")
  3. while n > 0:
  4. yield n
  5. n -= 1
  6. print("发射")

分析下面语句的执行过程:

  1. g = countdown(5)
  2. print(g.__next__()) # 打印"倒计时开始" 返回5 (此时n=5)
  3. print(g.__next__()) # 返回4 (此时n=4)
  4. print(g.__next__()) # 返回3 (此时n=3)
  5. print(g.__next__()) # 返回2 (此时n=2)
  6. print(g.__next__()) # 返回1 (此时n=1)
  7. print(g.__next__()) # 打印"发射" 抛出StopIteration异常(此时n=0)

调用__next__()时函数执行内部代码,到yield关键字时暂停:

  1. g = countdown(5)
  2. print(g.__next__())

输出:

  1. 倒计时开始
  2. 5

生成器也是不能后退:

  1. g = countdown(5)
  2. print(g.__next__())
  3. print(g.__next__())
  4. for i in g:
  5. print(i)

输出:

  1. 倒计时开始
  2. 5
  3. 4
  4. -- for --
  5. 3
  6. 2
  7. 1
  8. 发射

每调用一次countdown(5)得到的都是不同的生成器

  1. for i in countdown(5):
  2. print(i)
  3.  
  4. for i in copuntdown(5):
  5. print(i)

输出:

  1. 5
  2. 5

下面的例子也是一样,每一次print中countdown(5)都是一个全新的生成器,所以打印出来的值都是5

  1. print(countdown(5).__next())
  2. print(countdown(5).__next())
  3. print(countdown(5).__next())

输出:

  1. 5
  2. 5
  3. 5

生成器表达式

我们之前学过列表推导式,是这样写的:

  1. >>> [i for i in range(10)]
  2. [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

这样来得到一个元素数量较小的列表是非常方便的,但是如果要创建一个元素数量巨大的列表,就不那么友好了

  1. >>> [i for i in range(10000000000)]
  2. ...

这个时候只要把[]换成()就把列表推导式 变成了生成器表达式,得到的就是一个生成器对象,就是这么神奇。

这就是第二种自造迭代器的方法。

  1. >>> (i for i in range(10))
  2. <generator object <genexpr> at 0x101fef6d0>

我们可以直接使用for循环遍历上面得到的生成器:

  1. >>> for i in (i for i in range(10)):
  2. ... print(i)
  3. ...
  4. 0
  5. 1
  6. 2
  7. 3
  8. 4
  9. 5
  10. 6
  11. 7
  12. 8
  13. 9

这样我们就能自信的创建个10000000000元素的生成器,不担心内存会爆了。

  1. >>> (i for i in range(10000000000))
  2. <generator object <genexpr> at 0x101fef728>

最后的总结:

Python面试题之Python生成器的更多相关文章

  1. 千万不要错过这几道Python面试题,Python面试题No16

    第1题: python下多线程的限制以及多进程中传递参数的方式? python多线程有个全局解释器锁(global interpreter lock),简称GIL,这个GIL并不是python的特性, ...

  2. Python面试题之Python面试题汇总

    在这篇文章中: Python基础篇 1:为什么学习Python 2:通过什么途径学习Python 3:谈谈对Python和其他语言的区别 Python的优势: 4:简述解释型和编译型编程语言 5:Py ...

  3. python面试题之Python支持什么数据类型?

    所属网站分类: 面试经典 > python 作者:外星人入侵 链接:http://www.pythonheidong.com/blog/article/67/ 来源:python黑洞网,专注py ...

  4. python面试题三:Python 网络编程与并发

    1 简述 OSI 七层协议. OSI七层协议模型主要是: 应用层(Application):为用户的应用程序(例如电子邮件.文件传输和终端仿真)提供网络服务. 表示层(Presentation):使用 ...

  5. python面试题二:Python 基础题

    1.位和字节的关系? Byte 字节 bit 位 1Byte = 8bit 2.b.B.KB.MB.GB 的关系? 1Byte = 8bit KB 1KB=1024B MB 1MB=1024KB GB ...

  6. Python面试题之python是一种什么语言及优缺点

    1.说说python是一种什么语言? 参考答案:python是一门动态解释性的强类型定义语言 编译型vs解释型 编译型优点:编译器一般会有预编译的过程对代码进行优化.因为编译只做一次,运行时不需要编译 ...

  7. python面试题之Python是如何进行内存管理的

    python内部使用引用计数,来保持追踪内存中的对象,Python内部记录了对象有多少个引用,即引用计数,当对象被创建时就创建了一个引用计数,当对象不再需要时,这个对象的引用计数为0时,它被垃圾回收. ...

  8. python面试题之python下多线程的限制

    python多线程有个全局解释器锁(global interpreter lock). 这个锁的意思是任一时间只能有一个线程使用解释器,跟单cpu跑多个程序一个意思,大家都是轮着用的,这叫“并发”,不 ...

  9. Python面试题之Python中的类和实例

    0x00 前言 类,在学习面向对象我们可以把类当成一种规范,这个思想就我个人的体会,感觉很重要,除了封装的功能外,类作为一种规范,我们自己可以定制的规范,从这个角度来看,在以后我们学习设计模式的时候, ...

随机推荐

  1. 开源内容管理系统Joomla3.5发布 基于PHP 7

    导读 作为深受广大站长喜爱的Joomla开源内容管理系统(Content Management System, CMS)正式推出3.5版本,这也是首个完全支持PHP 7语言开发的Joomla版本,基于 ...

  2. AsyncTask--远程图片获取与本地缓存

    对于客户端——服务器端应用,从远程获取图片算是经常要用的一个功能,而图片资源往往会消耗比较大的流量,对应用来说,如果处理不好这个问题,那会让用户很崩溃,不知不觉手机流量就用完了,等用户发现是你的应用消 ...

  3. 程序记录2(设置MapID)

    try{ INIT_PLUG I_MongoDB* i = NEW(MongoDB); /*[注] 若自定义错误消息的数组长度必需指定为MAX_ERROR_SIZE*/ //char errmsg[M ...

  4. 【BZOJ3417】Poi2013 Tales of seafaring 分层图BFS

    [BZOJ3417]Poi2013 Tales of seafaring Description 一个n点m边无向图,边权均为1,有k个询问 每次询问给出(s,t,d),要求回答是否存在一条从s到t的 ...

  5. 【BZOJ1895】Pku3580 supermemo Splay

    [BZOJ1895]Pku3580 supermemo Description 给出一个初始序列fA1;A2;:::Ang,要求你编写程序支持如下操作: 1. ADDxyD:给子序列fAx:::Ayg ...

  6. 测试csscss层叠顺序

    <!Doctype html><html lang="zh-CN"><head><meta charset="utf-8&quo ...

  7. 160616、jQuery插件之ajaxFileUpload及jqueryeasyui学习资料分享

    第一步:先引入jQuery与ajaxFileUpload插件.注意先后顺序,ajaxFileUpload插件依赖jquery <script src="${base}/mobile/j ...

  8. Python代码样例列表

    扫描左上角二维码,关注公众账号 数字货币量化投资,回复“1279”,获取以下600个Python经典例子源码 ├─algorithm│       Python用户推荐系统曼哈顿算法实现.py│    ...

  9. Poloniex API 文档

    Examples PHP wrapper by compcentral: http://pastebin.com/iuezwGRZ Python wrapper by oipminer: http:/ ...

  10. 剑指Offer——数组中出现次数超过一半的数字

    题目描述: 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2 ...