|
|
|
|
移动端

3.2.2 生成器

《自学Python:编程基础、科学计算及数据分析》第3章Python 进阶,在本章中,我们将学习 Python的一些进阶用法,包括函数的进阶,迭代器、生成器、装饰器、上下文管理器的使用,以及 Python中的变量作用域。本节为大家介绍生成器。

作者:李金来源:机械工业出版社|2018-05-04 13:34

技术沙龙 | 邀您于8月25日与国美/AWS/转转三位专家共同探讨小程序电商实战

3.2.2 生成器

使用类实现自定义类型迭代器比较麻烦,一个更简单的方法是使用生成器(Generator)得到自定义的迭代器。

与类定义不同,生成器使用函数的形式来定义,不过与函数不同,生成器使用 yield关键字返回值。

例如,对于 Collatz猜想,可以用生成器形式定义如下:

  1. In [1]: def collatz(n):  
  2.    ...:     while n != 1:  
  3.    ...:     if n % 2 == 0:  
  4.    ...:    n /= 2  
  5.    ...:     else:  
  6.    ...:    n = 3*n + 1  
  7.    ...:     yield n  
  8.    ...: 

这个生成器在 while循环结束前会用 yield生成多个值,每次生成的值,相当于迭代器对象.next()方法的返回值;当生成器不能生成出新值时,相当于迭代器对象.next()方法抛出了异常。

使用生成器进行 for循环:

  1. In [2]: for x in collatz(7):  
  2.    ...:     print x,  
  3.    ...:   
  4.  
  5. 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1 

生成器是一种特殊的迭代器,我们可以通过.__iter__()方法和.next()方法来验证:

  1. In [3]: c = collatz(7)   
  2. In [4]: c   
  3. Out[4]: <generator object collatz at 0x000000000487EA68>   
  4. In [5]: c.__iter__() is c   
  5. Out[5]: True   
  6. In [6]: c.next()   
  7. Out[6]: 22   
  8. In [7]: c.next()   
  9. Out[7]: 11 

生成器.next()方法的返回值就对应每次yield的返回值。

例如,我们定义一个只有两条yield语句的生成器:

  1. In [8]: def test_generator():  
  2.    ...:     yield 1  
  3.    ...:     yield 2  
  4.    ...:   
  5.  
  6. In [9]: g = test_generator() 

调用两次.next()方法:

  1. In [10]: g.next(), g.next()   
  2. Out[10]: (1, 2) 

当我们第3次调用.next()方法时,生成器抛出了一个StopIteration异常:

  1. In [11]: g.next()  
  2. StopIteration   Traceback (most recent call last)   
  3. <ipython-input-11-d7e53364a9a7> in <module>()   
  4. ----> 1 g.next()   
  5.  
  6. StopIteration:  

逆序函数也可以用生成器实现:

  1. In [12]: def reverse(data):  
  2. ...: for index in range(len(data)-1, -1, -1):  
  3. ...: yield data[index]  
  4. ...:  
  5. In [13]: for c in reversed('abcde'):  
  6. ...: print c  
  7. ...:  
  8. e  
  9. d  
  10. c  
  11. b  

可以看到,生成器的实现方式要比迭代器更简单。

基于 for循环的列表推导式中的内容,也是基于生成器实现的。

例如,对于某个列表推导式:

  1. In [14]: [x for x in range(10)]  
  2. Out[14]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 

中括号中的 for循环推导式是一个生成器对象:

  1. In [15]: (x for x in range(10))  
  2. Out[15]: <generator object <genexpr> at 0x000000000487EEE8> 

其中,小括号是为了防止歧义,并不是表示元组。推导式生成元组需要显式地调用tuple()函数:

  1. In [16]: tuple(x for x in range(10))  
  2. Out[16]: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) 

总之,使用生成器或者迭代器,不需要一次性保存序列的所有值,而只在需要的时候计算序列的下一个值,从而减少程序使用的内存空间。


喜欢的朋友可以添加我们的微信账号:

51CTO读书频道二维码


51CTO读书频道活动讨论群:365934973

【责任编辑:book TEL:(010)68476606】

回书目   上一节   下一节
点赞 0
分享:
大家都在看
猜你喜欢

读 书 +更多

Java网络编程精解

本书结合大量的典型实例,详细介绍了用Java来编写网络应用程序的技术。本书的范例都基于最新的JDK 1.5版本,书中内容包括:Java网络编程的...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊