通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含1000万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(Generator)。

要创建一个generator,有很多种方法。

第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

1. 简单的列表操作:

  1. >>> list1 = [x*x for x in range()]
  2. >>> list1
  3. [, , , , , , , , , ]
  4. >>> list2 = [*x for x in range()]
  5. >>> list2
  6. [, , , , , , , , , , , ]
  7. >>>

2. 生成器操作:

  1. >>> list3 = (x*x for x in range())
  2. >>> list3
  3. <generator object <genexpr> at 0x7f302fdb6cd0>
  4. >>> list1
  5. [, , , , , , , , , ]
  6. >>> list2
  7. [, , , , , , , , , , , ]
  8. >>>

如代码所示,list1和list2是一个列表,而list3是一个generator,在任意时刻我们可以随便打印出list1和list2的列表元素,但是我们怎么才可以打印出list3的元素呢?,在这里generator提供一个打印自身元素的函数,next()。

  1. >>> list3
  2. <generator object <genexpr> at 0x7f302fdb6cd0>
  3. >>> list3.next()
  4.  
  5. >>> list3.next()
  6.  
  7. >>> list3.next()
  8.  
  9. >>> list3.next()
  10.  
  11. >>> list3.next()
  12.  
  13. >>> list3.next()
  14.  
  15. >>> list3.next()
  16.  
  17. >>> list3.next()
  18.  
  19. >>> list3.next()
  20.  
  21. >>> list3.next()
  22.  
  23. >>> list3.next()
  24. Traceback (most recent call last):
  25. File "<stdin>", line , in <module>
  26. StopIteration
  27. >>>

generator保存的是算法,每次调用next(),就计算出下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

使用next方法虽然可以计算出生成器的值,但是,实在是太不人性化了,在此,还有没有更好的方法来输出生成器的值呢?方法肯定是有的,那就是采用for迭代输出:

  1. >>> list4 = (x*x for x in range())
  2. >>> list3
  3. <generator object <genexpr> at 0x7f302fdb6cd0>
  4. >>> for n in list4:
  5. ... print n
  6. ...
  7.  
  8. >>>

所以,我们创建了一个generator后,基本上永远不会调用next()方法,而是通过for循环来迭代它。

generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。

比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:

1, 1, 2, 3, 5, 8, 13, 21, 34, ...

斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:

  1. >>>
  2. >>> def fibla(max):
  3. ... n,a,b = ,,
  4. ... while n < max:
  5. ... print b
  6. ... a,b = b,a+b
  7. ... n = n +
  8. ...
  9. >>> fibla()
  10.  
  11. >>>

观察之后发现,fibla函数其实是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print b改为yield b就可以了:

  1. >>> def fibla(max):
  2. ... n,a,b = ,,
  3. ... while n < max:
  4. ... print b
  5. ... a,b = b,a+b
  6. ... n = n +
  7. ...
  8. >>> fibla()
  9.  
  10. #上下对比
  11. >>> def fibla(max):
  12. ... n,a,b = ,,
  13. ... while n < max:
  14. ... yield b
  15. ... a,b = b,a+b
  16. ... n = n +
  17. ...
  18. >>> fibla()
  19. <generator object fibla at 0x7f302fd5ca00>
  20. >>>

这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:

这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

  1. >>> def fibla(max):
  2. ... n,a,b = ,,
  3. ... while n < max:
  4. ... yield b
  5. ... a,b = b,a+b
  6. ... n = n +
  7. ...
  8. >>> fibla()
  9. <generator object fibla at 0x7f302fd5ca00>
  10. >>> for x in fibla():
  11. ... print x
  12. ...
  13.  
  14. >>>

python学习之---生成器的更多相关文章

  1. python学习之生成器

    4.6 生成器Generrator ​ 生成器本质就是迭代器.python社区生成器与迭代器是一种. ​ 生成器与迭代器的唯一区别:生成器是我们自己用python代码构建的 4.6.1生成器初识 py ...

  2. Python学习二(生成器和八皇后算法)

    看书看到迭代器和生成器了,一般的使用是没什么问题的,不过很多时候并不能用的很习惯 书中例举了经典的八皇后问题,作为一个程序员怎么能够放过做题的机会呢,于是乎先自己来一遍,于是有了下面这个ugly的代码 ...

  3. Python学习笔记 - 生成器generator

    #!/usr/bin/env python3 # -*- coding: utf-8 -*- # generator 生成器 L = [x * x for x in range(10)] print( ...

  4. Python学习——迭代器&生成器&装饰器

    一.迭代器 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素.迭代器仅 ...

  5. python学习-39 生成器总结

    总结 1.语法上和函数类似:生成器函数和常规函数几乎是一样的.它们都是使用def语句进行定义,差别在于,生成器使用yield语句返回一个值,常规函数使用return语句返回一个值. 2.自动实现迭代器 ...

  6. python学习之- 生成器/迭代器

    列表生成式写法: [ i*2 for i in range(10) ]也可以带函数 [ fun(i) for i in range(10) ] 生成器:一边循环一边计算的机制称为生成器.在常用函数中, ...

  7. Python学习---装饰器/迭代器/生成器的学习【all】

    Python学习---装饰器的学习1210 Python学习---生成器的学习1210 Python学习---迭代器学习1210

  8. Python学习笔记之生成器、迭代器和装饰器

    这篇文章主要介绍 Python 中几个常用的高级特性,用好这几个特性可以让自己的代码更加 Pythonnic 哦 1.生成器 什么是生成器呢?简单来说,在 Python 中一边循环一边计算的机制称为 ...

  9. python学习10—迭代器、三元表达式与生成器

    python学习10—迭代器.三元表达式与生成器 1. 迭代器协议 定义:对象必须提供一个next方法,执行该方法或者返回迭代中的下一项,或者返回一个StopIteration异常,以终止迭代(只能往 ...

随机推荐

  1. Java基础知识强化之IO流笔记53:IO流练习之 自定义类模拟BufferedReader的readLine()功能案例

    1. 用Reader模拟BufferedReader的readLine()功能:   readLine():一次读取一行,根据换行符判断是否结束,只返回内容,不返回换行符 2. 代码实现和思路分析: ...

  2. 存储过程为什么比sql效率高

    对于存储过程为什么比sql效率高的原因有4点 第一就是使用存储过程允许组建式编成, 二是可以对程序进行编译,

  3. quartz多个scheduler实现

    1.首先排除错误用法 SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = sche ...

  4. [Form Builder]Oracle Form系统变量中文版总结大全

    转:http://yedward.net/?id=57 Form中的系统变量,它存在于一个Form的整个运行时期的会话之中,变量包含了有关Form相关属性的字节信息.有些变量标明了当前状态,还有些变量 ...

  5. 转:Oracle EBS订单的流程(Order->AR)

    目录(?)[-] 基本流程 创建订单 Pick Release Pick Confirm Ship Confirm Deliveries Interface Trip Stop - SRS Workf ...

  6. (整理)FORM:Oracle EBS客制化Form的菜单栏用法说明

    用户最多可以定义45个form-level的trigger,名称必须为SPECIALn, 其中SPECIAL1 to SPECIAL15属于Tools菜单项,放在“工具”主菜单下. SPECIAL16 ...

  7. RPC框架之Thrift

    目前流行的服务调用方式有很多种,例如基于SOAP消息格式的 Web Service,基于 JSON 消息格式的 RESTful 服务等.其中所用到的数据传输方式包括 XML,JSON 等,然而 XML ...

  8. Android开发之Intent跳转到系统应用中的拨号界面、联系人界面、短信界面

    现在开发中的功能需要直接跳转到拨号.联系人.短信界面等等,查找了很多资料,自己整理了一下. 1.跳转到拨号界面,代码如下: 1)直接拨打 Intent intentPhone = new Intent ...

  9. MVC中实现部分内容异步加载

    MVC中实现部分内容异步加载 action中定义一个得到结果集的方法 public ActionResult GetItemTree(string title, int itemid, int? pa ...

  10. ASP.NET页面生命周期总结(1)

    图解:1) 浏览器   :把用户的操作封装成一个请求通过socket发送到后台服务器. 后台服务器:首先有个内核模块Http.sys 和针对每个应用程序池都有一个请求队列.然后请求到达http.sys ...