前言
前一段时间和同事聊到Python技术知识,发现自己对生成器,迭代器傻傻分不清楚,于是乎查文档,找资料,有了此文。
通过本文大家可以了解到迭代器,生成器,生成器表达式,容器的定义以及关系。
一图胜千言
关系图(http://nvie.com/posts/iterators-vs-generators/)
先对上面的关系进行解释说明
生成器包括生成器表达(generator expression)和生成器函数(generator function)。 生成器(generator)是迭代器(iterator),但是反过来不一定成立,同时生成器也是可迭代的。 迭代器(iterator)都是可迭代的(iterable),并且实现了next()/__next()__方法。 元组,列表,集合构成容器这些对象都是可迭代的。
接下来我们深入浅出的去了解迭代器,生成器是什么,如何使用。
可迭代对象
相信大家都知道迭代的含义,就是可以循环遍历。 那什么是可迭代对象?通俗的将就是可以使用for x in iterable_obj 或者while 循环遍历的对象,比如list,set,tuple,dict等对象。我们可以通过isinstance()方法来判断,参考例子:
代码语言:javascript复制In [30]: from collections import Iterable
In [31]: isinstance('python', Iterable)
Out[31]: True
In [32]: s=3 # s为数字,不可迭代
In [33]: isinstance(s, Iterable)
Out[33]: False
In [35]: l = [1,2,3,4] #列表可用for循环遍历,可迭代
In [35]: isinstance(l, Iterable)
Out[35]: True
In [37]: w=(1,2,3) #元组可用for循环遍历,可迭代
In [38]: isinstance(w, Iterable)
Out[38]: True
从上面的检测来看 s=3 ,s是一个数值,不可迭代。其他的对象都是可以被循环访问的,即可迭代。
迭代器
迭代器是可以被next()函数调用并返回下一个值的对象,即Iterator。一个对象可迭代,是否就说明它是迭代器呢?我们继续使用isinstance 检测
代码语言:javascript复制In [37]: s=[1,2,3] #列表可以用for循环遍历,可迭代
In [39]: from collections import Iterable,Iterator
In [40]: isinstance(s, Iterator)
Out[40]: False ##
从结果来看s是一个列表,可迭代的对象(iterable)不一定是迭代器(iterator)。为什么列表,集合,字典等对象是可迭代但是不是迭代器呢?
Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。 我们可以把iterator当做有序序列,但我们不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,只有在需要返回下一个数据时它才会计算,即(lazy evaluation)。构造有几百万个值的列表所占用的内存大于几十M,而迭代器对象占用几十个字节的空间。
如何构造迭代器呢?本文介绍两种方式:
- 为容器对象添加 __iter__() 和 __next__() 方法(Python 2.7 中是 next());__iter__() 返回迭代器对象本身 self,__next__() 则返回每次调用 next() 或迭代时的元素; class MyIterator(object): def __init__(self, max): self.max = max # 上边界 self.now = 0 # 当前迭代值,初始为0 def __iter__(self): return self # 返回迭代器本身 def next(self): # 迭代器类必须实现的方法next() while self.now < self.max: self.now = 1 return self.now - 1 #返回当前迭代值 raise StopIteration #超出上边界,抛出异常 In [51]: my=MyIterator(10) In [52]: isinstance(my,Iterable) Out[52]: True In [53]: isinstance(my,Iterator) Out[53]: True
- python内置函数 iter() 将可迭代对象转化为迭代器。 l 是一个列表,通过iter() 函数将列表对象转化为迭代器。 In [45]: isinstance(l, Iterator) Out[45]: False In [46]: isinstance(iter(l), Iterator) Out[46]: True
生成器
生成器是迭代器的一种,不过生成器不需要实现__iter__()和__next__(),只要使用yield关键字返回值。当一个生成器函数调用yield,生成器函数的"状态"会被冻结,所有的变量的值会被保留下来,下一行要执行的代码的位置也会被记录,直到再次调用next()。一旦next()再次被调用,生成器函数会从它上次离开的地方开始。如果永远不调用next(),yield保存的状态就被无视了。从下面的例子可以出来yield运行机制:
代码语言:javascript复制 In [79]: def gen():
...: yield 1
...: yield 2
...: yield 3
...:
In [80]: g=gen()
In [81]: next(g)
Out[81]: 1
In [82]: next(g)
Out[82]: 2
In [83]: next(g)
Out[83]: 3
In [84]: next(g)
----------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-85-5f315c5de15b> in <module>()
----> 1 next(g)
StopIteration:
在python中生成器有两种:
- 生成器表达式 把列表生成式的[]中改为(),就创建一个generator In [90]: generator_expression = (x for x in range(5)) In [91]: type(generator_expression) Out[91]: generator In [92]: next(generator_expression) Out[92]: 0 In [93]: next(generator_expression) Out[93]: 1
- 生成器函数 In [99]: def fib(max): ...: n,a,b =0,0,1 ...: while n < max: ...: yield b ## 关键 ...: a,b =b,a b ...: n = n 1 ...: In [102]: type(f) Out[102]: generator In [103]: print f <generator object fib at 0x1097d1cd0> In [104]: f=fib(6) In [105]: next(f) Out[105]: 1 In [106]: next(f) Out[106]: 1 In [107]: next(f) Out[107]: 2 In [108]: next(f) Out[108]: 3 从102 步骤可以看出函数fib返回一个生成器对象,函数fib在每次调用next()的时候执行,遇到yield语句返回,再次被next()调用时候从上次的返回yield语句处继续执行,也就是用多少,取多少,不占内存。
总结
- 函数如果定义返回的话,必须一次返回所有的结果,因为函数只能返回一次。生成器因为使用了 yield关键字,保存执行到yield的上下文,再次调用的时候可以直接继续执行下一步操作。
- 生成器是特殊的迭代器,只能执行一次。
- 生成器和迭代器两者都是可迭代对象。
- yield是一个类似return 的关键字,迭代一次遇到yield的时候就返回yield后面或者右面的值。而且下一次迭代的时候,从上一次迭代遇到的yield后面的代码开始执行
推荐阅读
https://www.cnblogs.com/wj-1314/p/8490822.html
http://python.jobbole.com/87613/
http://python.jobbole.com/84527/
http://python.jobbole.com/87805/
http://python.jobbole.com/87312/
https://www.zhihu.com/question/20829330
https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014317799226173f45ce40636141b6abc8424e12b5fb27000
https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001386832360548a6491f20c62d427287739fcfa5d5be1f000