小白教程

 找回密码
 立即注册
查看: 8506|回复: 0

小白教程:Python 生成器 and 迭代器笔记

[复制链接]

176

主题

185

帖子

663

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
663
发表于 2021-4-9 12:52:36 | 显示全部楼层 |阅读模式
列表推导

假设现在有一个需求,要求我们把列表 [0,1,2,3,4,5,6,7,8,9] 中的每个值加 1,那么一般会有以下几种实现方式:

方法一(直接使用 for 循环,不推荐):

  1. info = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  2. for i, ele in enumerate(info):
  3.     info[i] += 1
  4. print(info)
复制代码

方法二(使用内置的 map 函数):

  1. info = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  2. a = map(lambda x:x+1, info)
  3. print(a)
复制代码

方法三(列表推导):

  1. info = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  2. a = [i+1 for i in range(10)]
  3. print(a)
复制代码

其中,更推荐使用方法三,写法更加的 Pythonic,而且当需要条件过滤时,map 需要借助 filter 函数,使用列表推导的方式更简洁。


生成器

通过上述的列表推导可以方便地创建一个列表,但是需要创建的列表很大时,就会占据很大的内存,极端情况下内存甚至无法容纳,而且如果我们仅仅只是需要访问当中某些元素,更是一个极大的浪费,此时,我们就需要考虑用生成器,而不是直接创建并返回列表。


生成器类似于返回值为数组的一个函数,这个函数可以接受参数,可以被调用,但是,不同于一般的函数会一次性返回包括了所有数值的数组,生成器一次只能产生一个值,这样消耗的内存数量将大大减小,而且允许调用函数可以很快的处理前几个返回值,因此生成器看起来像是一个函数,但是表现得却像是迭代器。


要创建一个生成器,有很多种方法,第一种方法就是把列表推导的 [] 换成 (),就创建了一个生成器:

  1. >>> L = [x * x for x in range(10)]
  2. >>> L
  3. [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
  4. >>> g = (x * x for x in range(10))
  5. >>> g
  6. <generator object <genexpr> at 0x1022ef630>
复制代码

生成器中值无法像列表一样直接打印,需要通过 next() 函数获取生成器的下一个返回值:

  1. >>> next(g)
  2. 0
  3. >>> next(g)
  4. 1
  5. >>> next(g)
  6. 4
  7. >>> next(g)
  8. 9
  9. >>> next(g)
  10. 16
  11. >>> next(g)
  12. 25
  13. >>> next(g)
  14. 36
  15. >>> next(g)
  16. 49
  17. >>> next(g)
  18. 64
  19. >>> next(g)
  20. 81
  21. >>> next(g)
  22. Traceback (most recent call last):
  23.   File "<stdin>", line 1, in <module>
  24. StopIteration
复制代码

可以看到,生成器保存的其实是产生元素的算法,每次调用 next(g),就计算出 g 的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出 StopIteration 的错误。因为生成器本身也是可迭代对象,所以一般使用 for 循环:

  1. >>> g = (x * x for x in range(10))
  2. >>> for n in g:
  3. ...     print(n)
  4. ...
  5. 0
  6. 1
  7. 4
  8. 9
  9. 16
  10. 25
  11. 36
  12. 49
  13. 64
  14. 81
复制代码

当需要实现的算法比较复杂,列表推导无法很好表达的情况下,则需要通过生成器函数来定义,例如著名的斐波那契数列:

  1. def fib(max):
  2.     n, a, b = 0, 0, 1
  3.     while n < max:
  4.         yield b
  5.         a, b = b, a + b
  6.         n = n + 1
复制代码

这就是实现生成器的第二种方式,如果一个函数定义中包含 yield 关键字,那么这个函数就不再是一个普通函数,而是一个生成器。


这里说一下生成器和函数的执行流程,函数是顺序执行的,遇到 return 语句或者最后一行函数语句就返回。而变成生成器的函数,在每次调用 next() 的时候执行,遇到 yield 语句返回,再次被 next() 调用时候从上次的返回 yield 语句处急需执行,也就是用多少,取多少,不占内存。


同样的,既然是生成器,就可以用 for 循环来迭代:

  1. >>> for n in fib(6):
  2. ...     print(n)
  3. ...
  4. 1
  5. 1
  6. 2
  7. 3
  8. 5
  9. 8
复制代码

单总结,Python 提供了两种方法来实现生成器

  • 生成器函数:也是用 def 定义的,利用关键字 yield 一次性返回一个结果,阻塞,重新开始
  • 生成器表达式:返回一个生成器对象,这个对象只有在需要的时候才产生结果

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|小白教程 ( 粤ICP备20019910号 )

GMT+8, 2024-9-20 15:02 , Processed in 0.611182 second(s), 22 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc. Template By 【未来科技】【 www.wekei.cn 】

快速回复 返回顶部 返回列表